JSF詳解(從jsf的運(yùn)行原理開(kāi)始介紹了jsf)
1. ? 結(jié)構(gòu):
a) ? ? 結(jié)構(gòu)圖:
? ?
b) ? ? 說(shuō)明:JSF以MVC模式為基礎(chǔ),與Struts不同,JSF的目標(biāo)是希望以一個(gè)與Swing相類似的方式來(lái)開(kāi)發(fā)網(wǎng)頁(yè),因此,從JSF的結(jié)構(gòu)圖當(dāng)中,他的核心概念不是頁(yè)面,而是控件樹,也就是說(shuō),當(dāng)用戶提交一個(gè)請(qǐng)求時(shí),JSF會(huì)先將頁(yè)面上的組件先轉(zhuǎn)換為與Swing當(dāng)中類似的,由容器和控件組成的控件樹,然后數(shù)據(jù)和事件被設(shè)置到對(duì)應(yīng)的控件上,然后以一種與Swing類似的方式,來(lái)處理后續(xù)的請(qǐng)求。控件樹是整個(gè)JSF的核心,所有其他的一切一切都是圍繞著這棵控件樹展開(kāi)的。
2. ? 生命周期:
a) ? ? 周期圖:
?
? b) ? ? 說(shuō)明:
? b) ? ? 說(shuō)明:
? b) ? ? 說(shuō)明:
? ? ? ? ? ? ? i. ? ? ? ? Restore View:JSF的處理核心是控件樹,他會(huì)先將頁(yè)面上所聲明的控件轉(zhuǎn)換為一棵控件樹,后續(xù)的操作將在這顆控件樹上進(jìn)行。為了提高性能,系統(tǒng)會(huì)為之前生成的控件樹提供緩存。Restore View的工作就是在緩存當(dāng)中查找是否存在之前已經(jīng)生成好的控件樹,如果沒(méi)有,則根據(jù)頁(yè)面的內(nèi)容,重新生成。
? ? ? ? ? ? ii. ? ? ? ? Apply Request Values:把請(qǐng)求當(dāng)中的數(shù)據(jù)設(shè)置到控件樹當(dāng)中對(duì)應(yīng)的控件當(dāng)中去。
? ? ? ? ? iii. ? ? ? ? Process Validations:如果某一控件有配置Validator,則這些Validator將對(duì)剛設(shè)置的數(shù)據(jù)的正確性和合法性進(jìn)行驗(yàn)證。
? ? ? ? ? ? iv. ? ? ? ? Update Model Values:控件樹上的控件更新其底層所對(duì)應(yīng)的模型。
? ? ? ? ? ? ? v. ? ? ? ? Invoke Application:對(duì)產(chǎn)生的事件進(jìn)行分發(fā)。
? ? ? ? ? ? vi. ? ? ? ? Render Response:構(gòu)建作為響應(yīng)的控件樹。
3. ? UI:
a) ? ? 結(jié)構(gòu)圖:

b) ? ? 控件:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:JSF通過(guò)標(biāo)簽庫(kù),提供了一些主要控件的實(shí)現(xiàn)。包括標(biāo)簽,文本框,單選框,列表等。由于JSF使用一種類似于UI的方式來(lái)組織組件,所以,除了基本的組件以外,還提供了一些用于布局的容器,例如面板等。在這里有一個(gè)要注意的地方就是,一般情況下,頁(yè)面的內(nèi)容應(yīng)該放到JSF提供的view標(biāo)簽里面。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<body>
<f:view>
<h:form>
<h:panelGrid columns="1">
<h:outputLabel>
<h:outputText value="User ID"/>
</h:outputLabel>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
c) ? ? 事件處理:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:與Struts不同,由于JSF使用以控件樹為中心的方式來(lái)處理請(qǐng)求,所以,她提供了一種額外的類似Swing的,事件處理的方式來(lái)處理用戶的輸入事件。JSF提供了兩種事件類型,ActionEvent,用于處理命令和ValueChangeEvent,用于處理數(shù)據(jù)更改。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
模型代碼:
package nick;
public class UserActionListener implements ActionListener {
? public void processAction(ActionEvent arg0)
throws AbortProcessingException {
? ? FacesContext context = FacesContext.getCurrentInstance();
? ? ValueBinding binding = Util.getValueBinding("#{user}");
? ? User user = (User) binding.getValue(context);
? ? String id = user.getId();
? }
}
頁(yè)面代碼:
<%@page contentType="text/html;charset=gb2312" import="java.util.*"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<body>
<f:view>
<h:form>
<h:panelGrid columns="2">
<h:outputLabel for="id">
<h:outputText value="User ID"/>
</h:outputLabel>
<h:commandButton id="regist" value="注冊(cè)">
<f:actionListener type="nick.UserActionListener"/>
</h:commandButton>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:通過(guò)嵌套actionListener標(biāo)簽,我們可以為一個(gè)控件注冊(cè)監(jiān)視器。
4. ? 數(shù)據(jù)綁定:
a) ? ? 說(shuō)明:數(shù)據(jù)綁定要解決的問(wèn)題就是如何把模型中的值,綁定到頁(yè)面的控件上。在JSF當(dāng)中這可以通過(guò)JSF所提供的配置文件來(lái)完成。
b) ? ? 代碼:
配置文件:
<faces-config>
<managed-bean>
<managed-bean-name> user </managed-bean-name>
<managed-bean-class> nick.User </managed-bean-class>
<managed-bean-scope> session </managed-bean-scope>
</managed-bean>
</faces-config>
模型代碼:
package nick;
public class User {
? private String id = "Nick";
public void setId(String id) {
? ? this.id = id;
? }
? public String getId() {
? ? return this.id;
? }
}
頁(yè)面代碼:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
? <head><title>Test</title></head>
<body>
<f:view>
<h:form>
<h:panelGrid columns="2">
<h:outputLabel for="id">
<h:outputText value="User ID"/>
</h:outputLabel>
<h:inputText id="id" value="#{user.id}"/>
? ? ? ? ? </h:panelGrid>
? ? ? ? </h:form>
? ? </f:view>
? </body> ?
</html>
注:通過(guò)配置文件,我們把nick.User類綁定到名稱user上,然后頁(yè)面的代碼就可以直接使用#{user.xxx}來(lái)引用User這個(gè)類中的各個(gè)字段。
5. ? 頁(yè)面流:
a) ? ? 頁(yè)面到控制器:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:JSF通過(guò)使用方法綁定的方式來(lái)定義從頁(yè)面到控制器的跳轉(zhuǎn),和數(shù)據(jù)綁定相同,為了能夠正確找到被綁定方法所在的類,我們需要首先在配置文件當(dāng)中聲明managed-bean,然后通過(guò)設(shè)置控件的action屬性,定義頁(yè)面到控制器的跳轉(zhuǎn)邏輯。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
配置文件:
<faces-config>
<managed-bean>
<managed-bean-name> user </managed-bean-name>
<managed-bean-class> nick.User </managed-bean-class>
<managed-bean-scope> session </managed-bean-scope>
</managed-bean>
</faces-config>
模型代碼:
package nick;
public class User {
? public String regist() {
? ? return "regist";
? }
? public String login() {
? ? return "login";
? }
}
頁(yè)面代碼:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<body>
<f:view>
<h:form>
<h:panelGrid columns="2">
<h:commandButton id="regist"
action="#{user.regist}" value="注冊(cè)"/>
<h:commandButton id="login"
action="#{user.login}" value="登陸"/>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:上述的頁(yè)面代碼,把注冊(cè)按鈕的動(dòng)作綁定到User類的regist()方法,把登陸按鈕的動(dòng)作綁定到User類的login()方法。因此,當(dāng)這兩個(gè)按鈕被點(diǎn)擊時(shí),對(duì)應(yīng)的方法將被調(diào)用,用于實(shí)現(xiàn)頁(yè)面流的方法,必須聲明為public,而且她不接受參數(shù),且返回值必須為String。
b) ? ? 控制器到頁(yè)面:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:JSF通過(guò)名稱綁定的方式,來(lái)定義從控制器到頁(yè)面的跳轉(zhuǎn)。為了實(shí)現(xiàn)從控制器到頁(yè)面的跳轉(zhuǎn),我們需要在配置文件當(dāng)中定義一些<navigation-rule>,這些rule主要定義了怎么根據(jù)上述action標(biāo)簽所綁定的方法的返回值來(lái)查找下一頁(yè)面。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
配置文件:
<faces-config>
<managed-bean>
<managed-bean-name> user </managed-bean-name>
<managed-bean-class> nick.User </managed-bean-class>
<managed-bean-scope> session </managed-bean-scope>
</managed-bean>
<navigation-rule>
<from-view-id>/index.jsp</from-view-id>
<navigation-case>
<from-outcome>regist</from-outcome>
<to-view-id>/regist.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>login</from-outcome>
<to-view-id>/login.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
模型代碼:
package nick;
public class User {
? public String regist() {
? ? return "regist";
? }
? public String login() {
? ? return "login";
? }
}
注:上述的配置文件定義了一個(gè)<navigation-rule>,該rule指明了如果“/index.jsp”頁(yè)面通過(guò)她內(nèi)部的某個(gè)控件的action屬性發(fā)生了跳轉(zhuǎn),那么當(dāng)該跳轉(zhuǎn)方法的返回值為字符串“regist”時(shí),則頁(yè)面將跳轉(zhuǎn)到對(duì)應(yīng)的“/regist.jsp”中,同理,如果返回值為“l(fā)ogin”,則頁(yè)面將跳轉(zhuǎn)到“/login.jsp”。
6. ? 數(shù)據(jù)傳輸:
a) ? ? 頁(yè)面到控制器:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:在JSF的頁(yè)面代碼當(dāng)中,通過(guò)數(shù)據(jù)綁定,我們把控件的value值,與某個(gè)后臺(tái)的數(shù)據(jù)bean關(guān)聯(lián)起來(lái)。而在前述的生命周期部分,我們看到,當(dāng)一個(gè)JSF請(qǐng)求到達(dá)時(shí),他需要經(jīng)歷Restore View,Apply Request Value等步驟,而Apply Request Value部分的工作,就是把請(qǐng)求當(dāng)中的值綁定到這個(gè)后臺(tái)的bean之中,因此,我們不需要考慮頁(yè)面中的Form值如何傳入到后臺(tái)的bean當(dāng)中。
進(jìn)一步,如果錄入控件的value屬性,和命令控件的action屬性都是綁定在同一個(gè)bean上的話,那么在頁(yè)面跳轉(zhuǎn)時(shí),我們可以直接訪問(wèn)到bean的屬性值。但是為了不污染模型,和實(shí)現(xiàn)控制與模型的分離,一般情況下,我們需要把輸入控件的value值綁定到數(shù)據(jù)bean,而把命令控件的action值綁定到控制bean,由于兩個(gè)bean不是同一個(gè),所以,控制bean需要一種方法來(lái)獲取數(shù)據(jù)bean中的屬性值。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
配置文件:
<faces-config>
<managed-bean>
<managed-bean-name> user </managed-bean-name>
<managed-bean-class> nick.User </managed-bean-class>
<managed-bean-scope> session </managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name> action </managed-bean-name>
<managed-bean-class> nick.Action </managed-bean-class>
<managed-bean-scope> session </managed-bean-scope>
</managed-bean>
</faces-config>
模型代碼:
? ? ? ? package nick;
public class User {
private String id = "Nick";
public void setId(String id) {
this.id = id;
}
public String getId() {
return this.id;
}
}
package nick;
public class Action {
? public String regist() {
? ? ValueBinding binding = Util.getValueBinding("#{user}");
? ? User user = (User)
binding.getValue(FacesContext.getCurrentInstance());
? ? ? ? ? ? …
? ? return "regist";
? }
}
頁(yè)面代碼:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<body>
<f:view>
<h:form>
<h:panelGrid columns="2">
<h:outputLabel for="id">
<h:outputText value="User ID"/>
</h:outputLabel>
<h:inputText id="id" value="#{user.id}"/>
<h:commandButton id="regist"
action="#{action.regist}" value="注冊(cè)"/>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:頁(yè)面代碼當(dāng)中把輸入控件的value綁定到了user的id上,把命令控件的action值綁
定到action的regist上。當(dāng)用戶點(diǎn)擊登陸按鈕時(shí),action的regist()方法將會(huì)被調(diào)用,
而在該方法內(nèi)部,為了獲取之前頁(yè)面中的信息,我們可以使用ValueBinding類。該類使用
的數(shù)據(jù)綁定表達(dá)式與頁(yè)面中的類似。
b) ? ? 控制器到頁(yè)面:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:通過(guò)之前的敘述,我們可以發(fā)現(xiàn),當(dāng)我們需要把數(shù)據(jù)從控制器傳到頁(yè)面時(shí),我們同樣可以使用數(shù)據(jù)綁定的方式,因?yàn)椋?dāng)我們通過(guò)數(shù)據(jù)綁定表達(dá)式,獲取到某個(gè)頁(yè)面當(dāng)中所使用的模型實(shí)例時(shí),便可以在該實(shí)例上直接調(diào)用對(duì)應(yīng)的set()方法,來(lái)設(shè)定所希望的值。
7. ? 插件功能:
a) ? ? Converter(轉(zhuǎn)換):
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:當(dāng)模型中的某個(gè)字段需要在頁(yè)面上顯示時(shí),她需要先被轉(zhuǎn)換為字符串類型;而用戶在頁(yè)面輸入的字符串值,在傳到模型中時(shí)也需要根據(jù)模型對(duì)應(yīng)字段的類型,進(jìn)行一個(gè)轉(zhuǎn)換。另外,由于國(guó)際化的要去,模型中的值在不同的地區(qū)會(huì)有不同的表示方式。為了解決以上這些問(wèn)題,JSF提供了Converter的實(shí)現(xiàn),她主要做的事情就是根據(jù)所在的地區(qū),對(duì)頁(yè)面數(shù)據(jù)和模型數(shù)據(jù)進(jìn)行雙向的轉(zhuǎn)換。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
模型代碼:
package nick;
public class User {
? private Date date = new Date();
? public Date getDate() {
? ? return date;
? }
? public void setDate(Date date) {
? ? this.date = date;
? }
}
頁(yè)面代碼:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<body>
<f:view>
<h:form>
<h:panelGrid columns="3">
<h:outputLabel for="date">
<h:outputText value="date"/>
</h:outputLabel>
<h:inputText id="date" value="#{user.date}">
<f:convertDateTime pattern="M/d/yyyy"/>
</h:inputText>
<h:message for="date"/>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:通過(guò)嵌套的convertXXX標(biāo)簽,我們可以為控件配置用于轉(zhuǎn)換的轉(zhuǎn)換器。我們可以使用兩種方式來(lái)注冊(cè)轉(zhuǎn)換器,一是通過(guò)控件的convert屬性,另外一種就是通過(guò)嵌套的convertXXX標(biāo)簽。如果在轉(zhuǎn)換的時(shí)候發(fā)生錯(cuò)誤,那么JSF將跳過(guò)轉(zhuǎn)換以后的步驟,而直接跳到Render Response步驟,生成響應(yīng),并在FacesContext里添加一個(gè)出錯(cuò)的Message,該Message的內(nèi)容可以通過(guò)message標(biāo)簽進(jìn)行顯示。
b) ? ? Validate(驗(yàn)證):
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:在數(shù)據(jù)被交付后臺(tái)處理以前,我們可以通過(guò)驗(yàn)證器,來(lái)驗(yàn)證輸入的數(shù)據(jù)是否合法,這包括數(shù)值的大小,或者是字符串的長(zhǎng)度等。使用驗(yàn)證器的好處就是我們可以把驗(yàn)證的代碼從控制代碼中單獨(dú)出來(lái),以便管理和重用。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
頁(yè)面代碼:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<body>
<f:view>
<h:form>
<h:panelGrid columns="3">
<h:outputLabel for="id">
<h:outputText value="User ID"/>
</h:outputLabel>
<h:inputText id="id" value="#{user.id}">
<f:validateLength minimum="3" maximum="6"/>
</h:inputText>
<h:message for="id"/>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:通過(guò)嵌套的validateXXX標(biāo)簽我們可以為一個(gè)控件注冊(cè)一個(gè)驗(yàn)證器,與轉(zhuǎn)換器不同,我們可以為一個(gè)控件注冊(cè)多個(gè)驗(yàn)證器,以進(jìn)行復(fù)雜的驗(yàn)證。注冊(cè)驗(yàn)證器的方法也有兩個(gè),一是通過(guò)控件的validator屬性,另外一種就是通過(guò)嵌套的validateXXX標(biāo)簽。與轉(zhuǎn)換器類似,當(dāng)驗(yàn)證失敗時(shí),失敗的原因也會(huì)以Message的形式被放到FacesContext內(nèi),在頁(yè)面內(nèi)通過(guò)message標(biāo)簽可以對(duì)錯(cuò)誤信息進(jìn)行顯示。
8. ? 國(guó)際化支持
a) ? ? 說(shuō)明:為了提供對(duì)國(guó)際化的支持,JSF使用了與Struts類似的通過(guò)資源文件的形式,從外部獲取需要顯示的內(nèi)容。
b) ? ? 代碼:
配置文件:
<faces-config>
<application>
<message-bundle>nick.Messages</message-bundle>
<locale-config>
<default-locale>cn</default-locale>
<supported-locale>en</supported-locale>
</locale-config>
? </application>
</faces-config>
頁(yè)面代碼:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<f:loadBundle basename="nick.Messages" var="message"/>
<body>
<f:view>
<h:form>
<h:panelGrid columns="3">
<h:outputLabel for="id">
<h:outputText value="#{message.id}"/>
</h:outputLabel>
<h:inputText id="id" value="#{user.id}"/>
? ? ? ? ? ? ? ? </h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:如果我們需要考慮國(guó)際化的問(wèn)題時(shí),那么我們需要為同一個(gè)資源提供多個(gè)地區(qū)的資源文件實(shí)現(xiàn)。通過(guò)在配置文件中聲明message-bundle,我們可以注冊(cè)資源文件的多個(gè)版本,以上面為例,她注冊(cè)了兩個(gè)資源文件nick.Messages_cn.properties和nickcen.Messages_en.properties分別對(duì)應(yīng)于中國(guó)和英國(guó)地區(qū),當(dāng)頁(yè)面當(dāng)中通過(guò)loadBundle標(biāo)簽讀取資源時(shí),她就會(huì)根據(jù)所在的區(qū)域,去查找對(duì)應(yīng)的文件。當(dāng)然,所有資源文件都應(yīng)該使用java所提供的native2ascii工具,進(jìn)行一下預(yù)處理。
9. ? JSF定制:
a) ? ? 說(shuō)明:該部分主要展示如何對(duì)JSF提供的轉(zhuǎn)換器,驗(yàn)證器和控件進(jìn)行定制,以擴(kuò)展JSF的功能。
b) ? ? 配置文件定制:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:JSF提供了比Struts更多的定制特性,包括用于實(shí)例化前端控制FacesServelt的faces-context-factory,用于實(shí)例化控件繪畫RenderKit的render-kit-factory,進(jìn)行聲明周期管理的lifecyle-factory等。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
配置文件:
<faces-config>
? <factory>
? ? <application-factory>…</application-factory>
? ? <faces-context-factory>…</faces-context-factory>
? ? <lifecycle-factory>…</lifecycle-factory>
? ? <render-kit-factory>…</render-kit-factory>
? </factory>
</faces-config>
c) ? ? 轉(zhuǎn)換器定制:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:通過(guò)擴(kuò)展JSF提供的Converter接口,我們可以定義自己的轉(zhuǎn)換器。而為了能在頁(yè)面上使用自定義的轉(zhuǎn)換器,我們需要在配置文件中,對(duì)轉(zhuǎn)換器進(jìn)行注冊(cè)。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
配置文件:
<faces-config>
<converter>
<converter-id>nameConverter</converter-id>
<converter-class>nick.NameConverter</converter-class>
</converter>
</faces-config>
模型文件:
package nick;
public class NameConverter implements Converter {
public Object getAsObject(FacesContext ar0, UIComponent ar1, String ar2) {
? ? return ar2.toLowerCase();
}
public String getAsString(FacesContext ar0, UIComponent ar1, Object ar2) {
? ? retrun ((String) arg2).toUpperCase();
}
}
頁(yè)面文件:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<f:loadBundle basename="nick.Messages" var="message"/>
<body>
<f:view>
<h:form>
<h:panelGrid columns="3">
<h:outputLabel for="id">
<h:outputText value="#{message.id}"/>
</h:outputLabel>
<h:inputText id="id"
value="#{user.id}" immediate="true">
<f:converter converterId="nameConverter"/>
</h:inputText>
<h:message for="id"/>
<h:commandButton
action="#{action.regist}" value="Regist"/>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:當(dāng)頁(yè)面數(shù)據(jù)轉(zhuǎn)換為后臺(tái)數(shù)據(jù)時(shí),Converter的getAsObject()方法會(huì)被調(diào)用。而當(dāng)需要把后臺(tái)數(shù)據(jù)顯示到頁(yè)面中時(shí),getAsString()方法會(huì)被調(diào)用。這里,我們注意到,我們的Converter是不能帶任何配置屬性的,為了讓我們的Converter能夠在頁(yè)面中增加屬性,我們需要進(jìn)一步的定義自己的標(biāo)簽。除了可以根據(jù)converter-id來(lái)注冊(cè)轉(zhuǎn)換器以外,我們還可以使用converter-for-class標(biāo)簽來(lái)為某一特定的Java類型注冊(cè)轉(zhuǎn)換器。如果在轉(zhuǎn)換過(guò)程中發(fā)生錯(cuò)誤的話,那么我們可以通過(guò)拋出ConverterException來(lái)停止后續(xù)的工作。
d) ? ? 驗(yàn)證器定制:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:通過(guò)擴(kuò)展Validator接口,我們可以定義自己的驗(yàn)證器。而為了能在頁(yè)面上使用自定義的驗(yàn)證器,我們需要在配置文件中,對(duì)驗(yàn)證器進(jìn)行注冊(cè)。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
配置文件:
<faces-config>
<validator>
<validator-id>nameValidator</validator-id>
<validator-class>nick.NameValidator</validator-class>
</validator>
</faces-config>
模型文件:
package nick;
public class NameValidator implements Validator {
? public void validate(FacesContext arg0, UIComponent arg1, Object arg2)
? ? ? ? throws ValidatorException {
? ? String name = (String) arg2;
? ? if (!name.startsWith("nick")) {
? ? ? ? throw new ValidatorException(new FacesMessage(
? ? ? ? ? ? "name must start with nick"));
? ? }
? }
}
頁(yè)面文件:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<f:loadBundle basename="nick.Messages" var="message"/>
<body>
<f:view>
<h:form>
<h:panelGrid columns="3">
<h:outputLabel for="id">
<h:outputText value="#{message.id}"/>
</h:outputLabel>
<h:inputText id="id" value="#{user.id}"> ? ? ? ? ? ? <f:validator validatorId="nameValidator"/>
</h:inputText>
<h:message for="id"/>
<h:commandButton
action="#{action.regist}" value="Regist"/>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:定制驗(yàn)證器的操作與定制轉(zhuǎn)換器的操作大致相同,如果我們希望通過(guò)頁(yè)面的方式為驗(yàn)證器提供屬性的話,那么我們也需要定義額外的標(biāo)簽。
e) ? ? 控件定制:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:JSF允許用戶基于JSF的框架,進(jìn)行控件的定制,控件定制需要完成以下幾部分的工作,1.需要一個(gè)控件實(shí)現(xiàn)類,為了能讓控件在頁(yè)面上使用,我們的控件還需要在配置文件中,通過(guò)component標(biāo)簽進(jìn)行注冊(cè);2.用于控件渲染的Render類;3.用于在頁(yè)面上使用控件的標(biāo)簽。
f) ? ? 控件樹定制:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:除了可以通過(guò)頁(yè)面標(biāo)簽來(lái)聲明控件以外,我們可以通過(guò)程序的方式來(lái)定制控件樹,通過(guò)調(diào)用FacesContext上的getViewRoot()方法,我們可以獲得控件樹的根引用,通過(guò)該引用,我們可以動(dòng)態(tài)的對(duì)控件進(jìn)行增刪,或者動(dòng)態(tài)的為某一控件增刪事件監(jiān)聽(tīng)器,和驗(yàn)證器等。
g) ? ? Web常量:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:JSF提供了一組操作application,context,session,cookies,page等常量的方法。通過(guò)調(diào)用FacesContext上的getExternalContext()方法,我們可以獲得一個(gè)對(duì)ExternalContext的引用,通過(guò)該引用我們可以獲取所有我們希望使用的session,cookies等常量的引用,除此以外,我們還能獲取request當(dāng)中的頭信息和請(qǐng)求信息等。
1. ? 結(jié)構(gòu):
a) ? ? 結(jié)構(gòu)圖:

b) ? ? 說(shuō)明:JSF以MVC模式為基礎(chǔ),與Struts不同,JSF的目標(biāo)是希望以一個(gè)與Swing相類似的方式來(lái)開(kāi)發(fā)網(wǎng)頁(yè),因此,從JSF的結(jié)構(gòu)圖當(dāng)中,他的核心概念不是頁(yè)面,而是控件樹,也就是說(shuō),當(dāng)用戶提交一個(gè)請(qǐng)求時(shí),JSF會(huì)先將頁(yè)面上的組件先轉(zhuǎn)換為與Swing當(dāng)中類似的,由容器和控件組成的控件樹,然后數(shù)據(jù)和事件被設(shè)置到對(duì)應(yīng)的控件上,然后以一種與Swing類似的方式,來(lái)處理后續(xù)的請(qǐng)求。控件樹是整個(gè)JSF的核心,所有其他的一切一切都是圍繞著這棵控件樹展開(kāi)的。
2. ? 生命周期:
a) ? ? 周期圖:

? b) ? ? 說(shuō)明:
? b) ? ? 說(shuō)明:
? b) ? ? 說(shuō)明:
? ? ? ? ? ? ? i. ? ? ? ? Restore View:JSF的處理核心是控件樹,他會(huì)先將頁(yè)面上所聲明的控件轉(zhuǎn)換為一棵控件樹,后續(xù)的操作將在這顆控件樹上進(jìn)行。為了提高性能,系統(tǒng)會(huì)為之前生成的控件樹提供緩存。Restore View的工作就是在緩存當(dāng)中查找是否存在之前已經(jīng)生成好的控件樹,如果沒(méi)有,則根據(jù)頁(yè)面的內(nèi)容,重新生成。
? ? ? ? ? ? ii. ? ? ? ? Apply Request Values:把請(qǐng)求當(dāng)中的數(shù)據(jù)設(shè)置到控件樹當(dāng)中對(duì)應(yīng)的控件當(dāng)中去。
? ? ? ? ? iii. ? ? ? ? Process Validations:如果某一控件有配置Validator,則這些Validator將對(duì)剛設(shè)置的數(shù)據(jù)的正確性和合法性進(jìn)行驗(yàn)證。
? ? ? ? ? ? iv. ? ? ? ? Update Model Values:控件樹上的控件更新其底層所對(duì)應(yīng)的模型。
? ? ? ? ? ? ? v. ? ? ? ? Invoke Application:對(duì)產(chǎn)生的事件進(jìn)行分發(fā)。
? ? ? ? ? ? vi. ? ? ? ? Render Response:構(gòu)建作為響應(yīng)的控件樹。
3. ? UI:
a) ? ? 結(jié)構(gòu)圖:

b) ? ? 控件:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:JSF通過(guò)標(biāo)簽庫(kù),提供了一些主要控件的實(shí)現(xiàn)。包括標(biāo)簽,文本框,單選框,列表等。由于JSF使用一種類似于UI的方式來(lái)組織組件,所以,除了基本的組件以外,還提供了一些用于布局的容器,例如面板等。在這里有一個(gè)要注意的地方就是,一般情況下,頁(yè)面的內(nèi)容應(yīng)該放到JSF提供的view標(biāo)簽里面。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<body>
<f:view>
<h:form>
<h:panelGrid columns="1">
<h:outputLabel>
<h:outputText value="User ID"/>
</h:outputLabel>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
c) ? ? 事件處理:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:與Struts不同,由于JSF使用以控件樹為中心的方式來(lái)處理請(qǐng)求,所以,她提供了一種額外的類似Swing的,事件處理的方式來(lái)處理用戶的輸入事件。JSF提供了兩種事件類型,ActionEvent,用于處理命令和ValueChangeEvent,用于處理數(shù)據(jù)更改。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
模型代碼:
package nick;
public class UserActionListener implements ActionListener {
? public void processAction(ActionEvent arg0)
throws AbortProcessingException {
? ? FacesContext context = FacesContext.getCurrentInstance();
? ? ValueBinding binding = Util.getValueBinding("#{user}");
? ? User user = (User) binding.getValue(context);
? ? String id = user.getId();
? }
}
頁(yè)面代碼:
<%@page contentType="text/html;charset=gb2312" import="java.util.*"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<body>
<f:view>
<h:form>
<h:panelGrid columns="2">
<h:outputLabel for="id">
<h:outputText value="User ID"/>
</h:outputLabel>
<h:commandButton id="regist" value="注冊(cè)">
<f:actionListener type="nick.UserActionListener"/>
</h:commandButton>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:通過(guò)嵌套actionListener標(biāo)簽,我們可以為一個(gè)控件注冊(cè)監(jiān)視器。
4. ? 數(shù)據(jù)綁定:
a) ? ? 說(shuō)明:數(shù)據(jù)綁定要解決的問(wèn)題就是如何把模型中的值,綁定到頁(yè)面的控件上。在JSF當(dāng)中這可以通過(guò)JSF所提供的配置文件來(lái)完成。
b) ? ? 代碼:
配置文件:
<faces-config>
<managed-bean>
<managed-bean-name> user </managed-bean-name>
<managed-bean-class> nick.User </managed-bean-class>
<managed-bean-scope> session </managed-bean-scope>
</managed-bean>
</faces-config>
模型代碼:
package nick;
public class User {
? private String id = "Nick";
public void setId(String id) {
? ? this.id = id;
? }
? public String getId() {
? ? return this.id;
? }
}
頁(yè)面代碼:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
? <head><title>Test</title></head>
<body>
<f:view>
<h:form>
<h:panelGrid columns="2">
<h:outputLabel for="id">
<h:outputText value="User ID"/>
</h:outputLabel>
<h:inputText id="id" value="#{user.id}"/>
? ? ? ? ? </h:panelGrid>
? ? ? ? </h:form>
? ? </f:view>
? </body> ?
</html>
注:通過(guò)配置文件,我們把nick.User類綁定到名稱user上,然后頁(yè)面的代碼就可以直接使用#{user.xxx}來(lái)引用User這個(gè)類中的各個(gè)字段。
5. ? 頁(yè)面流:
a) ? ? 頁(yè)面到控制器:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:JSF通過(guò)使用方法綁定的方式來(lái)定義從頁(yè)面到控制器的跳轉(zhuǎn),和數(shù)據(jù)綁定相同,為了能夠正確找到被綁定方法所在的類,我們需要首先在配置文件當(dāng)中聲明managed-bean,然后通過(guò)設(shè)置控件的action屬性,定義頁(yè)面到控制器的跳轉(zhuǎn)邏輯。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
配置文件:
<faces-config>
<managed-bean>
<managed-bean-name> user </managed-bean-name>
<managed-bean-class> nick.User </managed-bean-class>
<managed-bean-scope> session </managed-bean-scope>
</managed-bean>
</faces-config>
模型代碼:
package nick;
public class User {
? public String regist() {
? ? return "regist";
? }
? public String login() {
? ? return "login";
? }
}
頁(yè)面代碼:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<body>
<f:view>
<h:form>
<h:panelGrid columns="2">
<h:commandButton id="regist"
action="#{user.regist}" value="注冊(cè)"/>
<h:commandButton id="login"
action="#{user.login}" value="登陸"/>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:上述的頁(yè)面代碼,把注冊(cè)按鈕的動(dòng)作綁定到User類的regist()方法,把登陸按鈕的動(dòng)作綁定到User類的login()方法。因此,當(dāng)這兩個(gè)按鈕被點(diǎn)擊時(shí),對(duì)應(yīng)的方法將被調(diào)用,用于實(shí)現(xiàn)頁(yè)面流的方法,必須聲明為public,而且她不接受參數(shù),且返回值必須為String。
b) ? ? 控制器到頁(yè)面:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:JSF通過(guò)名稱綁定的方式,來(lái)定義從控制器到頁(yè)面的跳轉(zhuǎn)。為了實(shí)現(xiàn)從控制器到頁(yè)面的跳轉(zhuǎn),我們需要在配置文件當(dāng)中定義一些<navigation-rule>,這些rule主要定義了怎么根據(jù)上述action標(biāo)簽所綁定的方法的返回值來(lái)查找下一頁(yè)面。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
配置文件:
<faces-config>
<managed-bean>
<managed-bean-name> user </managed-bean-name>
<managed-bean-class> nick.User </managed-bean-class>
<managed-bean-scope> session </managed-bean-scope>
</managed-bean>
<navigation-rule>
<from-view-id>/index.jsp</from-view-id>
<navigation-case>
<from-outcome>regist</from-outcome>
<to-view-id>/regist.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>login</from-outcome>
<to-view-id>/login.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
模型代碼:
package nick;
public class User {
? public String regist() {
? ? return "regist";
? }
? public String login() {
? ? return "login";
? }
}
注:上述的配置文件定義了一個(gè)<navigation-rule>,該rule指明了如果“/index.jsp”頁(yè)面通過(guò)她內(nèi)部的某個(gè)控件的action屬性發(fā)生了跳轉(zhuǎn),那么當(dāng)該跳轉(zhuǎn)方法的返回值為字符串“regist”時(shí),則頁(yè)面將跳轉(zhuǎn)到對(duì)應(yīng)的“/regist.jsp”中,同理,如果返回值為“l(fā)ogin”,則頁(yè)面將跳轉(zhuǎn)到“/login.jsp”。
6. ? 數(shù)據(jù)傳輸:
a) ? ? 頁(yè)面到控制器:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:在JSF的頁(yè)面代碼當(dāng)中,通過(guò)數(shù)據(jù)綁定,我們把控件的value值,與某個(gè)后臺(tái)的數(shù)據(jù)bean關(guān)聯(lián)起來(lái)。而在前述的生命周期部分,我們看到,當(dāng)一個(gè)JSF請(qǐng)求到達(dá)時(shí),他需要經(jīng)歷Restore View,Apply Request Value等步驟,而Apply Request Value部分的工作,就是把請(qǐng)求當(dāng)中的值綁定到這個(gè)后臺(tái)的bean之中,因此,我們不需要考慮頁(yè)面中的Form值如何傳入到后臺(tái)的bean當(dāng)中。
進(jìn)一步,如果錄入控件的value屬性,和命令控件的action屬性都是綁定在同一個(gè)bean上的話,那么在頁(yè)面跳轉(zhuǎn)時(shí),我們可以直接訪問(wèn)到bean的屬性值。但是為了不污染模型,和實(shí)現(xiàn)控制與模型的分離,一般情況下,我們需要把輸入控件的value值綁定到數(shù)據(jù)bean,而把命令控件的action值綁定到控制bean,由于兩個(gè)bean不是同一個(gè),所以,控制bean需要一種方法來(lái)獲取數(shù)據(jù)bean中的屬性值。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
配置文件:
<faces-config>
<managed-bean>
<managed-bean-name> user </managed-bean-name>
<managed-bean-class> nick.User </managed-bean-class>
<managed-bean-scope> session </managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name> action </managed-bean-name>
<managed-bean-class> nick.Action </managed-bean-class>
<managed-bean-scope> session </managed-bean-scope>
</managed-bean>
</faces-config>
模型代碼:
? ? ? ? package nick;
public class User {
private String id = "Nick";
public void setId(String id) {
this.id = id;
}
public String getId() {
return this.id;
}
}
package nick;
public class Action {
? public String regist() {
? ? ValueBinding binding = Util.getValueBinding("#{user}");
? ? User user = (User)
binding.getValue(FacesContext.getCurrentInstance());
? ? ? ? ? ? …
? ? return "regist";
? }
}
頁(yè)面代碼:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<body>
<f:view>
<h:form>
<h:panelGrid columns="2">
<h:outputLabel for="id">
<h:outputText value="User ID"/>
</h:outputLabel>
<h:inputText id="id" value="#{user.id}"/>
<h:commandButton id="regist"
action="#{action.regist}" value="注冊(cè)"/>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:頁(yè)面代碼當(dāng)中把輸入控件的value綁定到了user的id上,把命令控件的action值綁
定到action的regist上。當(dāng)用戶點(diǎn)擊登陸按鈕時(shí),action的regist()方法將會(huì)被調(diào)用,
而在該方法內(nèi)部,為了獲取之前頁(yè)面中的信息,我們可以使用ValueBinding類。該類使用
的數(shù)據(jù)綁定表達(dá)式與頁(yè)面中的類似。
b) ? ? 控制器到頁(yè)面:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:通過(guò)之前的敘述,我們可以發(fā)現(xiàn),當(dāng)我們需要把數(shù)據(jù)從控制器傳到頁(yè)面時(shí),我們同樣可以使用數(shù)據(jù)綁定的方式,因?yàn)椋?dāng)我們通過(guò)數(shù)據(jù)綁定表達(dá)式,獲取到某個(gè)頁(yè)面當(dāng)中所使用的模型實(shí)例時(shí),便可以在該實(shí)例上直接調(diào)用對(duì)應(yīng)的set()方法,來(lái)設(shè)定所希望的值。
7. ? 插件功能:
a) ? ? Converter(轉(zhuǎn)換):
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:當(dāng)模型中的某個(gè)字段需要在頁(yè)面上顯示時(shí),她需要先被轉(zhuǎn)換為字符串類型;而用戶在頁(yè)面輸入的字符串值,在傳到模型中時(shí)也需要根據(jù)模型對(duì)應(yīng)字段的類型,進(jìn)行一個(gè)轉(zhuǎn)換。另外,由于國(guó)際化的要去,模型中的值在不同的地區(qū)會(huì)有不同的表示方式。為了解決以上這些問(wèn)題,JSF提供了Converter的實(shí)現(xiàn),她主要做的事情就是根據(jù)所在的地區(qū),對(duì)頁(yè)面數(shù)據(jù)和模型數(shù)據(jù)進(jìn)行雙向的轉(zhuǎn)換。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
模型代碼:
package nick;
public class User {
? private Date date = new Date();
? public Date getDate() {
? ? return date;
? }
? public void setDate(Date date) {
? ? this.date = date;
? }
}
頁(yè)面代碼:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<body>
<f:view>
<h:form>
<h:panelGrid columns="3">
<h:outputLabel for="date">
<h:outputText value="date"/>
</h:outputLabel>
<h:inputText id="date" value="#{user.date}">
<f:convertDateTime pattern="M/d/yyyy"/>
</h:inputText>
<h:message for="date"/>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:通過(guò)嵌套的convertXXX標(biāo)簽,我們可以為控件配置用于轉(zhuǎn)換的轉(zhuǎn)換器。我們可以使用兩種方式來(lái)注冊(cè)轉(zhuǎn)換器,一是通過(guò)控件的convert屬性,另外一種就是通過(guò)嵌套的convertXXX標(biāo)簽。如果在轉(zhuǎn)換的時(shí)候發(fā)生錯(cuò)誤,那么JSF將跳過(guò)轉(zhuǎn)換以后的步驟,而直接跳到Render Response步驟,生成響應(yīng),并在FacesContext里添加一個(gè)出錯(cuò)的Message,該Message的內(nèi)容可以通過(guò)message標(biāo)簽進(jìn)行顯示。
b) ? ? Validate(驗(yàn)證):
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:在數(shù)據(jù)被交付后臺(tái)處理以前,我們可以通過(guò)驗(yàn)證器,來(lái)驗(yàn)證輸入的數(shù)據(jù)是否合法,這包括數(shù)值的大小,或者是字符串的長(zhǎng)度等。使用驗(yàn)證器的好處就是我們可以把驗(yàn)證的代碼從控制代碼中單獨(dú)出來(lái),以便管理和重用。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
頁(yè)面代碼:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<body>
<f:view>
<h:form>
<h:panelGrid columns="3">
<h:outputLabel for="id">
<h:outputText value="User ID"/>
</h:outputLabel>
<h:inputText id="id" value="#{user.id}">
<f:validateLength minimum="3" maximum="6"/>
</h:inputText>
<h:message for="id"/>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:通過(guò)嵌套的validateXXX標(biāo)簽我們可以為一個(gè)控件注冊(cè)一個(gè)驗(yàn)證器,與轉(zhuǎn)換器不同,我們可以為一個(gè)控件注冊(cè)多個(gè)驗(yàn)證器,以進(jìn)行復(fù)雜的驗(yàn)證。注冊(cè)驗(yàn)證器的方法也有兩個(gè),一是通過(guò)控件的validator屬性,另外一種就是通過(guò)嵌套的validateXXX標(biāo)簽。與轉(zhuǎn)換器類似,當(dāng)驗(yàn)證失敗時(shí),失敗的原因也會(huì)以Message的形式被放到FacesContext內(nèi),在頁(yè)面內(nèi)通過(guò)message標(biāo)簽可以對(duì)錯(cuò)誤信息進(jìn)行顯示。
8. ? 國(guó)際化支持
a) ? ? 說(shuō)明:為了提供對(duì)國(guó)際化的支持,JSF使用了與Struts類似的通過(guò)資源文件的形式,從外部獲取需要顯示的內(nèi)容。
b) ? ? 代碼:
配置文件:
<faces-config>
<application>
<message-bundle>nick.Messages</message-bundle>
<locale-config>
<default-locale>cn</default-locale>
<supported-locale>en</supported-locale>
</locale-config>
? </application>
</faces-config>
頁(yè)面代碼:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<f:loadBundle basename="nick.Messages" var="message"/>
<body>
<f:view>
<h:form>
<h:panelGrid columns="3">
<h:outputLabel for="id">
<h:outputText value="#{message.id}"/>
</h:outputLabel>
<h:inputText id="id" value="#{user.id}"/>
? ? ? ? ? ? ? ? </h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:如果我們需要考慮國(guó)際化的問(wèn)題時(shí),那么我們需要為同一個(gè)資源提供多個(gè)地區(qū)的資源文件實(shí)現(xiàn)。通過(guò)在配置文件中聲明message-bundle,我們可以注冊(cè)資源文件的多個(gè)版本,以上面為例,她注冊(cè)了兩個(gè)資源文件nick.Messages_cn.properties和nickcen.Messages_en.properties分別對(duì)應(yīng)于中國(guó)和英國(guó)地區(qū),當(dāng)頁(yè)面當(dāng)中通過(guò)loadBundle標(biāo)簽讀取資源時(shí),她就會(huì)根據(jù)所在的區(qū)域,去查找對(duì)應(yīng)的文件。當(dāng)然,所有資源文件都應(yīng)該使用java所提供的native2ascii工具,進(jìn)行一下預(yù)處理。
9. ? JSF定制:
a) ? ? 說(shuō)明:該部分主要展示如何對(duì)JSF提供的轉(zhuǎn)換器,驗(yàn)證器和控件進(jìn)行定制,以擴(kuò)展JSF的功能。
b) ? ? 配置文件定制:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:JSF提供了比Struts更多的定制特性,包括用于實(shí)例化前端控制FacesServelt的faces-context-factory,用于實(shí)例化控件繪畫RenderKit的render-kit-factory,進(jìn)行聲明周期管理的lifecyle-factory等。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
配置文件:
<faces-config>
? <factory>
? ? <application-factory>…</application-factory>
? ? <faces-context-factory>…</faces-context-factory>
? ? <lifecycle-factory>…</lifecycle-factory>
? ? <render-kit-factory>…</render-kit-factory>
? </factory>
</faces-config>
c) ? ? 轉(zhuǎn)換器定制:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:通過(guò)擴(kuò)展JSF提供的Converter接口,我們可以定義自己的轉(zhuǎn)換器。而為了能在頁(yè)面上使用自定義的轉(zhuǎn)換器,我們需要在配置文件中,對(duì)轉(zhuǎn)換器進(jìn)行注冊(cè)。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
配置文件:
<faces-config>
<converter>
<converter-id>nameConverter</converter-id>
<converter-class>nick.NameConverter</converter-class>
</converter>
</faces-config>
模型文件:
package nick;
public class NameConverter implements Converter {
public Object getAsObject(FacesContext ar0, UIComponent ar1, String ar2) {
? ? return ar2.toLowerCase();
}
public String getAsString(FacesContext ar0, UIComponent ar1, Object ar2) {
? ? retrun ((String) arg2).toUpperCase();
}
}
頁(yè)面文件:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<f:loadBundle basename="nick.Messages" var="message"/>
<body>
<f:view>
<h:form>
<h:panelGrid columns="3">
<h:outputLabel for="id">
<h:outputText value="#{message.id}"/>
</h:outputLabel>
<h:inputText id="id"
value="#{user.id}" immediate="true">
<f:converter converterId="nameConverter"/>
</h:inputText>
<h:message for="id"/>
<h:commandButton
action="#{action.regist}" value="Regist"/>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:當(dāng)頁(yè)面數(shù)據(jù)轉(zhuǎn)換為后臺(tái)數(shù)據(jù)時(shí),Converter的getAsObject()方法會(huì)被調(diào)用。而當(dāng)需要把后臺(tái)數(shù)據(jù)顯示到頁(yè)面中時(shí),getAsString()方法會(huì)被調(diào)用。這里,我們注意到,我們的Converter是不能帶任何配置屬性的,為了讓我們的Converter能夠在頁(yè)面中增加屬性,我們需要進(jìn)一步的定義自己的標(biāo)簽。除了可以根據(jù)converter-id來(lái)注冊(cè)轉(zhuǎn)換器以外,我們還可以使用converter-for-class標(biāo)簽來(lái)為某一特定的Java類型注冊(cè)轉(zhuǎn)換器。如果在轉(zhuǎn)換過(guò)程中發(fā)生錯(cuò)誤的話,那么我們可以通過(guò)拋出ConverterException來(lái)停止后續(xù)的工作。
d) ? ? 驗(yàn)證器定制:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:通過(guò)擴(kuò)展Validator接口,我們可以定義自己的驗(yàn)證器。而為了能在頁(yè)面上使用自定義的驗(yàn)證器,我們需要在配置文件中,對(duì)驗(yàn)證器進(jìn)行注冊(cè)。
? ? ? ? ? ? ii. ? ? ? ? 代碼:
配置文件:
<faces-config>
<validator>
<validator-id>nameValidator</validator-id>
<validator-class>nick.NameValidator</validator-class>
</validator>
</faces-config>
模型文件:
package nick;
public class NameValidator implements Validator {
? public void validate(FacesContext arg0, UIComponent arg1, Object arg2)
? ? ? ? throws ValidatorException {
? ? String name = (String) arg2;
? ? if (!name.startsWith("nick")) {
? ? ? ? throw new ValidatorException(new FacesMessage(
? ? ? ? ? ? "name must start with nick"));
? ? }
? }
}
頁(yè)面文件:
<%@page contentType="text/html;charset=gb2312"%>
<%@ taglib uri=" http://java.sun.com/jsf/core " prefix="f"%>
<%@ taglib uri=" http://java.sun.com/jsf/html " prefix="h"%>
<html>
<head><title>Test</title></head>
<f:loadBundle basename="nick.Messages" var="message"/>
<body>
<f:view>
<h:form>
<h:panelGrid columns="3">
<h:outputLabel for="id">
<h:outputText value="#{message.id}"/>
</h:outputLabel>
<h:inputText id="id" value="#{user.id}"> ? ? ? ? ? ? <f:validator validatorId="nameValidator"/>
</h:inputText>
<h:message for="id"/>
<h:commandButton
action="#{action.regist}" value="Regist"/>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:定制驗(yàn)證器的操作與定制轉(zhuǎn)換器的操作大致相同,如果我們希望通過(guò)頁(yè)面的方式為驗(yàn)證器提供屬性的話,那么我們也需要定義額外的標(biāo)簽。
e) ? ? 控件定制:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:JSF允許用戶基于JSF的框架,進(jìn)行控件的定制,控件定制需要完成以下幾部分的工作,1.需要一個(gè)控件實(shí)現(xiàn)類,為了能讓控件在頁(yè)面上使用,我們的控件還需要在配置文件中,通過(guò)component標(biāo)簽進(jìn)行注冊(cè);2.用于控件渲染的Render類;3.用于在頁(yè)面上使用控件的標(biāo)簽。
f) ? ? 控件樹定制:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:除了可以通過(guò)頁(yè)面標(biāo)簽來(lái)聲明控件以外,我們可以通過(guò)程序的方式來(lái)定制控件樹,通過(guò)調(diào)用FacesContext上的getViewRoot()方法,我們可以獲得控件樹的根引用,通過(guò)該引用,我們可以動(dòng)態(tài)的對(duì)控件進(jìn)行增刪,或者動(dòng)態(tài)的為某一控件增刪事件監(jiān)聽(tīng)器,和驗(yàn)證器等。
g) ? ? Web常量:
? ? ? ? ? ? ? i. ? ? ? ? 說(shuō)明:JSF提供了一組操作application,context,session,cookies,page等常量的方法。通過(guò)調(diào)用FacesContext上的getExternalContext()方法,我們可以獲得一個(gè)對(duì)ExternalContext的引用,通過(guò)該引用我們可以獲取所有我們希望使用的session,cookies等常量的引用,除此以外,我們還能獲取request當(dāng)中的頭信息和請(qǐng)求信息等。