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

b) ? ? 控件:
? ? ? ? ? ? ? i. ? ? ? ? 說明:JSF通過標(biāo)簽庫,提供了一些主要控件的實現(xiàn)。包括標(biāo)簽,文本框,單選框,列表等。由于JSF使用一種類似于UI的方式來組織組件,所以,除了基本的組件以外,還提供了一些用于布局的容器,例如面板等。在這里有一個要注意的地方就是,一般情況下,頁面的內(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. ? ? ? ? 說明:與Struts不同,由于JSF使用以控件樹為中心的方式來處理請求,所以,她提供了一種額外的類似Swing的,事件處理的方式來處理用戶的輸入事件。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();
? }
}
頁面代碼:
<%@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="注冊">
<f:actionListener type="nick.UserActionListener"/>
</h:commandButton>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:通過嵌套actionListener標(biāo)簽,我們可以為一個控件注冊監(jiān)視器。
4. ? 數(shù)據(jù)綁定:
a) ? ? 說明:數(shù)據(jù)綁定要解決的問題就是如何把模型中的值,綁定到頁面的控件上。在JSF當(dāng)中這可以通過JSF所提供的配置文件來完成。
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;
? }
}
頁面代碼:
<%@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>
注:通過配置文件,我們把nick.User類綁定到名稱user上,然后頁面的代碼就可以直接使用#{user.xxx}來引用User這個類中的各個字段。
5. ? 頁面流:
a) ? ? 頁面到控制器:
? ? ? ? ? ? ? i. ? ? ? ? 說明:JSF通過使用方法綁定的方式來定義從頁面到控制器的跳轉(zhuǎn),和數(shù)據(jù)綁定相同,為了能夠正確找到被綁定方法所在的類,我們需要首先在配置文件當(dāng)中聲明managed-bean,然后通過設(shè)置控件的action屬性,定義頁面到控制器的跳轉(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";
? }
}
頁面代碼:
<%@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="注冊"/>
<h:commandButton id="login"
action="#{user.login}" value="登陸"/>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:上述的頁面代碼,把注冊按鈕的動作綁定到User類的regist()方法,把登陸按鈕的動作綁定到User類的login()方法。因此,當(dāng)這兩個按鈕被點擊時,對應(yīng)的方法將被調(diào)用,用于實現(xiàn)頁面流的方法,必須聲明為public,而且她不接受參數(shù),且返回值必須為String。
b) ? ? 控制器到頁面:
? ? ? ? ? ? ? i. ? ? ? ? 說明:JSF通過名稱綁定的方式,來定義從控制器到頁面的跳轉(zhuǎn)。為了實現(xiàn)從控制器到頁面的跳轉(zhuǎn),我們需要在配置文件當(dāng)中定義一些<navigation-rule>,這些rule主要定義了怎么根據(jù)上述action標(biāo)簽所綁定的方法的返回值來查找下一頁面。
? ? ? ? ? ? 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";
? }
}
注:上述的配置文件定義了一個<navigation-rule>,該rule指明了如果“/index.jsp”頁面通過她內(nèi)部的某個控件的action屬性發(fā)生了跳轉(zhuǎn),那么當(dāng)該跳轉(zhuǎn)方法的返回值為字符串“regist”時,則頁面將跳轉(zhuǎn)到對應(yīng)的“/regist.jsp”中,同理,如果返回值為“l(fā)ogin”,則頁面將跳轉(zhuǎn)到“/login.jsp”。
6. ? 數(shù)據(jù)傳輸:
a) ? ? 頁面到控制器:
? ? ? ? ? ? ? i. ? ? ? ? 說明:在JSF的頁面代碼當(dāng)中,通過數(shù)據(jù)綁定,我們把控件的value值,與某個后臺的數(shù)據(jù)bean關(guān)聯(lián)起來。而在前述的生命周期部分,我們看到,當(dāng)一個JSF請求到達(dá)時,他需要經(jīng)歷Restore View,Apply Request Value等步驟,而Apply Request Value部分的工作,就是把請求當(dāng)中的值綁定到這個后臺的bean之中,因此,我們不需要考慮頁面中的Form值如何傳入到后臺的bean當(dāng)中。
進(jìn)一步,如果錄入控件的value屬性,和命令控件的action屬性都是綁定在同一個bean上的話,那么在頁面跳轉(zhuǎn)時,我們可以直接訪問到bean的屬性值。但是為了不污染模型,和實現(xiàn)控制與模型的分離,一般情況下,我們需要把輸入控件的value值綁定到數(shù)據(jù)bean,而把命令控件的action值綁定到控制bean,由于兩個bean不是同一個,所以,控制bean需要一種方法來獲取數(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";
? }
}
頁面代碼:
<%@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="注冊"/>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:頁面代碼當(dāng)中把輸入控件的value綁定到了user的id上,把命令控件的action值綁
定到action的regist上。當(dāng)用戶點擊登陸按鈕時,action的regist()方法將會被調(diào)用,
而在該方法內(nèi)部,為了獲取之前頁面中的信息,我們可以使用ValueBinding類。該類使用
的數(shù)據(jù)綁定表達(dá)式與頁面中的類似。
b) ? ? 控制器到頁面:
? ? ? ? ? ? ? i. ? ? ? ? 說明:通過之前的敘述,我們可以發(fā)現(xiàn),當(dāng)我們需要把數(shù)據(jù)從控制器傳到頁面時,我們同樣可以使用數(shù)據(jù)綁定的方式,因為,當(dāng)我們通過數(shù)據(jù)綁定表達(dá)式,獲取到某個頁面當(dāng)中所使用的模型實例時,便可以在該實例上直接調(diào)用對應(yīng)的set()方法,來設(shè)定所希望的值。
7. ? 插件功能:
a) ? ? Converter(轉(zhuǎn)換):
? ? ? ? ? ? ? i. ? ? ? ? 說明:當(dāng)模型中的某個字段需要在頁面上顯示時,她需要先被轉(zhuǎn)換為字符串類型;而用戶在頁面輸入的字符串值,在傳到模型中時也需要根據(jù)模型對應(yīng)字段的類型,進(jìn)行一個轉(zhuǎn)換。另外,由于國際化的要去,模型中的值在不同的地區(qū)會有不同的表示方式。為了解決以上這些問題,JSF提供了Converter的實現(xiàn),她主要做的事情就是根據(jù)所在的地區(qū),對頁面數(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;
? }
}
頁面代碼:
<%@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>
注:通過嵌套的convertXXX標(biāo)簽,我們可以為控件配置用于轉(zhuǎn)換的轉(zhuǎn)換器。我們可以使用兩種方式來注冊轉(zhuǎn)換器,一是通過控件的convert屬性,另外一種就是通過嵌套的convertXXX標(biāo)簽。如果在轉(zhuǎn)換的時候發(fā)生錯誤,那么JSF將跳過轉(zhuǎn)換以后的步驟,而直接跳到Render Response步驟,生成響應(yīng),并在FacesContext里添加一個出錯的Message,該Message的內(nèi)容可以通過message標(biāo)簽進(jìn)行顯示。
b) ? ? Validate(驗證):
? ? ? ? ? ? ? i. ? ? ? ? 說明:在數(shù)據(jù)被交付后臺處理以前,我們可以通過驗證器,來驗證輸入的數(shù)據(jù)是否合法,這包括數(shù)值的大小,或者是字符串的長度等。使用驗證器的好處就是我們可以把驗證的代碼從控制代碼中單獨出來,以便管理和重用。
? ? ? ? ? ? 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="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>
注:通過嵌套的validateXXX標(biāo)簽我們可以為一個控件注冊一個驗證器,與轉(zhuǎn)換器不同,我們可以為一個控件注冊多個驗證器,以進(jìn)行復(fù)雜的驗證。注冊驗證器的方法也有兩個,一是通過控件的validator屬性,另外一種就是通過嵌套的validateXXX標(biāo)簽。與轉(zhuǎn)換器類似,當(dāng)驗證失敗時,失敗的原因也會以Message的形式被放到FacesContext內(nèi),在頁面內(nèi)通過message標(biāo)簽可以對錯誤信息進(jìn)行顯示。
8. ? 國際化支持
a) ? ? 說明:為了提供對國際化的支持,JSF使用了與Struts類似的通過資源文件的形式,從外部獲取需要顯示的內(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>
頁面代碼:
<%@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>
注:如果我們需要考慮國際化的問題時,那么我們需要為同一個資源提供多個地區(qū)的資源文件實現(xiàn)。通過在配置文件中聲明message-bundle,我們可以注冊資源文件的多個版本,以上面為例,她注冊了兩個資源文件nick.Messages_cn.properties和nickcen.Messages_en.properties分別對應(yīng)于中國和英國地區(qū),當(dāng)頁面當(dāng)中通過loadBundle標(biāo)簽讀取資源時,她就會根據(jù)所在的區(qū)域,去查找對應(yīng)的文件。當(dāng)然,所有資源文件都應(yīng)該使用java所提供的native2ascii工具,進(jìn)行一下預(yù)處理。
9. ? JSF定制:
a) ? ? 說明:該部分主要展示如何對JSF提供的轉(zhuǎn)換器,驗證器和控件進(jìn)行定制,以擴(kuò)展JSF的功能。
b) ? ? 配置文件定制:
? ? ? ? ? ? ? i. ? ? ? ? 說明:JSF提供了比Struts更多的定制特性,包括用于實例化前端控制FacesServelt的faces-context-factory,用于實例化控件繪畫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. ? ? ? ? 說明:通過擴(kuò)展JSF提供的Converter接口,我們可以定義自己的轉(zhuǎn)換器。而為了能在頁面上使用自定義的轉(zhuǎn)換器,我們需要在配置文件中,對轉(zhuǎn)換器進(jìn)行注冊。
? ? ? ? ? ? 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();
}
}
頁面文件:
<%@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)頁面數(shù)據(jù)轉(zhuǎn)換為后臺數(shù)據(jù)時,Converter的getAsObject()方法會被調(diào)用。而當(dāng)需要把后臺數(shù)據(jù)顯示到頁面中時,getAsString()方法會被調(diào)用。這里,我們注意到,我們的Converter是不能帶任何配置屬性的,為了讓我們的Converter能夠在頁面中增加屬性,我們需要進(jìn)一步的定義自己的標(biāo)簽。除了可以根據(jù)converter-id來注冊轉(zhuǎn)換器以外,我們還可以使用converter-for-class標(biāo)簽來為某一特定的Java類型注冊轉(zhuǎn)換器。如果在轉(zhuǎn)換過程中發(fā)生錯誤的話,那么我們可以通過拋出ConverterException來停止后續(xù)的工作。
d) ? ? 驗證器定制:
? ? ? ? ? ? ? i. ? ? ? ? 說明:通過擴(kuò)展Validator接口,我們可以定義自己的驗證器。而為了能在頁面上使用自定義的驗證器,我們需要在配置文件中,對驗證器進(jìn)行注冊。
? ? ? ? ? ? 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"));
? ? }
? }
}
頁面文件:
<%@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>
注:定制驗證器的操作與定制轉(zhuǎn)換器的操作大致相同,如果我們希望通過頁面的方式為驗證器提供屬性的話,那么我們也需要定義額外的標(biāo)簽。
e) ? ? 控件定制:
? ? ? ? ? ? ? i. ? ? ? ? 說明:JSF允許用戶基于JSF的框架,進(jìn)行控件的定制,控件定制需要完成以下幾部分的工作,1.需要一個控件實現(xiàn)類,為了能讓控件在頁面上使用,我們的控件還需要在配置文件中,通過component標(biāo)簽進(jìn)行注冊;2.用于控件渲染的Render類;3.用于在頁面上使用控件的標(biāo)簽。
f) ? ? 控件樹定制:
? ? ? ? ? ? ? i. ? ? ? ? 說明:除了可以通過頁面標(biāo)簽來聲明控件以外,我們可以通過程序的方式來定制控件樹,通過調(diào)用FacesContext上的getViewRoot()方法,我們可以獲得控件樹的根引用,通過該引用,我們可以動態(tài)的對控件進(jìn)行增刪,或者動態(tài)的為某一控件增刪事件監(jiān)聽器,和驗證器等。
g) ? ? Web常量:
? ? ? ? ? ? ? i. ? ? ? ? 說明:JSF提供了一組操作application,context,session,cookies,page等常量的方法。通過調(diào)用FacesContext上的getExternalContext()方法,我們可以獲得一個對ExternalContext的引用,通過該引用我們可以獲取所有我們希望使用的session,cookies等常量的引用,除此以外,我們還能獲取request當(dāng)中的頭信息和請求信息等。
1. ? 結(jié)構(gòu):
a) ? ? 結(jié)構(gòu)圖:

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

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

b) ? ? 控件:
? ? ? ? ? ? ? i. ? ? ? ? 說明:JSF通過標(biāo)簽庫,提供了一些主要控件的實現(xiàn)。包括標(biāo)簽,文本框,單選框,列表等。由于JSF使用一種類似于UI的方式來組織組件,所以,除了基本的組件以外,還提供了一些用于布局的容器,例如面板等。在這里有一個要注意的地方就是,一般情況下,頁面的內(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. ? ? ? ? 說明:與Struts不同,由于JSF使用以控件樹為中心的方式來處理請求,所以,她提供了一種額外的類似Swing的,事件處理的方式來處理用戶的輸入事件。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();
? }
}
頁面代碼:
<%@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="注冊">
<f:actionListener type="nick.UserActionListener"/>
</h:commandButton>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:通過嵌套actionListener標(biāo)簽,我們可以為一個控件注冊監(jiān)視器。
4. ? 數(shù)據(jù)綁定:
a) ? ? 說明:數(shù)據(jù)綁定要解決的問題就是如何把模型中的值,綁定到頁面的控件上。在JSF當(dāng)中這可以通過JSF所提供的配置文件來完成。
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;
? }
}
頁面代碼:
<%@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>
注:通過配置文件,我們把nick.User類綁定到名稱user上,然后頁面的代碼就可以直接使用#{user.xxx}來引用User這個類中的各個字段。
5. ? 頁面流:
a) ? ? 頁面到控制器:
? ? ? ? ? ? ? i. ? ? ? ? 說明:JSF通過使用方法綁定的方式來定義從頁面到控制器的跳轉(zhuǎn),和數(shù)據(jù)綁定相同,為了能夠正確找到被綁定方法所在的類,我們需要首先在配置文件當(dāng)中聲明managed-bean,然后通過設(shè)置控件的action屬性,定義頁面到控制器的跳轉(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";
? }
}
頁面代碼:
<%@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="注冊"/>
<h:commandButton id="login"
action="#{user.login}" value="登陸"/>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:上述的頁面代碼,把注冊按鈕的動作綁定到User類的regist()方法,把登陸按鈕的動作綁定到User類的login()方法。因此,當(dāng)這兩個按鈕被點擊時,對應(yīng)的方法將被調(diào)用,用于實現(xiàn)頁面流的方法,必須聲明為public,而且她不接受參數(shù),且返回值必須為String。
b) ? ? 控制器到頁面:
? ? ? ? ? ? ? i. ? ? ? ? 說明:JSF通過名稱綁定的方式,來定義從控制器到頁面的跳轉(zhuǎn)。為了實現(xiàn)從控制器到頁面的跳轉(zhuǎn),我們需要在配置文件當(dāng)中定義一些<navigation-rule>,這些rule主要定義了怎么根據(jù)上述action標(biāo)簽所綁定的方法的返回值來查找下一頁面。
? ? ? ? ? ? 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";
? }
}
注:上述的配置文件定義了一個<navigation-rule>,該rule指明了如果“/index.jsp”頁面通過她內(nèi)部的某個控件的action屬性發(fā)生了跳轉(zhuǎn),那么當(dāng)該跳轉(zhuǎn)方法的返回值為字符串“regist”時,則頁面將跳轉(zhuǎn)到對應(yīng)的“/regist.jsp”中,同理,如果返回值為“l(fā)ogin”,則頁面將跳轉(zhuǎn)到“/login.jsp”。
6. ? 數(shù)據(jù)傳輸:
a) ? ? 頁面到控制器:
? ? ? ? ? ? ? i. ? ? ? ? 說明:在JSF的頁面代碼當(dāng)中,通過數(shù)據(jù)綁定,我們把控件的value值,與某個后臺的數(shù)據(jù)bean關(guān)聯(lián)起來。而在前述的生命周期部分,我們看到,當(dāng)一個JSF請求到達(dá)時,他需要經(jīng)歷Restore View,Apply Request Value等步驟,而Apply Request Value部分的工作,就是把請求當(dāng)中的值綁定到這個后臺的bean之中,因此,我們不需要考慮頁面中的Form值如何傳入到后臺的bean當(dāng)中。
進(jìn)一步,如果錄入控件的value屬性,和命令控件的action屬性都是綁定在同一個bean上的話,那么在頁面跳轉(zhuǎn)時,我們可以直接訪問到bean的屬性值。但是為了不污染模型,和實現(xiàn)控制與模型的分離,一般情況下,我們需要把輸入控件的value值綁定到數(shù)據(jù)bean,而把命令控件的action值綁定到控制bean,由于兩個bean不是同一個,所以,控制bean需要一種方法來獲取數(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";
? }
}
頁面代碼:
<%@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="注冊"/>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>
注:頁面代碼當(dāng)中把輸入控件的value綁定到了user的id上,把命令控件的action值綁
定到action的regist上。當(dāng)用戶點擊登陸按鈕時,action的regist()方法將會被調(diào)用,
而在該方法內(nèi)部,為了獲取之前頁面中的信息,我們可以使用ValueBinding類。該類使用
的數(shù)據(jù)綁定表達(dá)式與頁面中的類似。
b) ? ? 控制器到頁面:
? ? ? ? ? ? ? i. ? ? ? ? 說明:通過之前的敘述,我們可以發(fā)現(xiàn),當(dāng)我們需要把數(shù)據(jù)從控制器傳到頁面時,我們同樣可以使用數(shù)據(jù)綁定的方式,因為,當(dāng)我們通過數(shù)據(jù)綁定表達(dá)式,獲取到某個頁面當(dāng)中所使用的模型實例時,便可以在該實例上直接調(diào)用對應(yīng)的set()方法,來設(shè)定所希望的值。
7. ? 插件功能:
a) ? ? Converter(轉(zhuǎn)換):
? ? ? ? ? ? ? i. ? ? ? ? 說明:當(dāng)模型中的某個字段需要在頁面上顯示時,她需要先被轉(zhuǎn)換為字符串類型;而用戶在頁面輸入的字符串值,在傳到模型中時也需要根據(jù)模型對應(yīng)字段的類型,進(jìn)行一個轉(zhuǎn)換。另外,由于國際化的要去,模型中的值在不同的地區(qū)會有不同的表示方式。為了解決以上這些問題,JSF提供了Converter的實現(xiàn),她主要做的事情就是根據(jù)所在的地區(qū),對頁面數(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;
? }
}
頁面代碼:
<%@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>
注:通過嵌套的convertXXX標(biāo)簽,我們可以為控件配置用于轉(zhuǎn)換的轉(zhuǎn)換器。我們可以使用兩種方式來注冊轉(zhuǎn)換器,一是通過控件的convert屬性,另外一種就是通過嵌套的convertXXX標(biāo)簽。如果在轉(zhuǎn)換的時候發(fā)生錯誤,那么JSF將跳過轉(zhuǎn)換以后的步驟,而直接跳到Render Response步驟,生成響應(yīng),并在FacesContext里添加一個出錯的Message,該Message的內(nèi)容可以通過message標(biāo)簽進(jìn)行顯示。
b) ? ? Validate(驗證):
? ? ? ? ? ? ? i. ? ? ? ? 說明:在數(shù)據(jù)被交付后臺處理以前,我們可以通過驗證器,來驗證輸入的數(shù)據(jù)是否合法,這包括數(shù)值的大小,或者是字符串的長度等。使用驗證器的好處就是我們可以把驗證的代碼從控制代碼中單獨出來,以便管理和重用。
? ? ? ? ? ? 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="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>
注:通過嵌套的validateXXX標(biāo)簽我們可以為一個控件注冊一個驗證器,與轉(zhuǎn)換器不同,我們可以為一個控件注冊多個驗證器,以進(jìn)行復(fù)雜的驗證。注冊驗證器的方法也有兩個,一是通過控件的validator屬性,另外一種就是通過嵌套的validateXXX標(biāo)簽。與轉(zhuǎn)換器類似,當(dāng)驗證失敗時,失敗的原因也會以Message的形式被放到FacesContext內(nèi),在頁面內(nèi)通過message標(biāo)簽可以對錯誤信息進(jìn)行顯示。
8. ? 國際化支持
a) ? ? 說明:為了提供對國際化的支持,JSF使用了與Struts類似的通過資源文件的形式,從外部獲取需要顯示的內(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>
頁面代碼:
<%@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>
注:如果我們需要考慮國際化的問題時,那么我們需要為同一個資源提供多個地區(qū)的資源文件實現(xiàn)。通過在配置文件中聲明message-bundle,我們可以注冊資源文件的多個版本,以上面為例,她注冊了兩個資源文件nick.Messages_cn.properties和nickcen.Messages_en.properties分別對應(yīng)于中國和英國地區(qū),當(dāng)頁面當(dāng)中通過loadBundle標(biāo)簽讀取資源時,她就會根據(jù)所在的區(qū)域,去查找對應(yīng)的文件。當(dāng)然,所有資源文件都應(yīng)該使用java所提供的native2ascii工具,進(jìn)行一下預(yù)處理。
9. ? JSF定制:
a) ? ? 說明:該部分主要展示如何對JSF提供的轉(zhuǎn)換器,驗證器和控件進(jìn)行定制,以擴(kuò)展JSF的功能。
b) ? ? 配置文件定制:
? ? ? ? ? ? ? i. ? ? ? ? 說明:JSF提供了比Struts更多的定制特性,包括用于實例化前端控制FacesServelt的faces-context-factory,用于實例化控件繪畫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. ? ? ? ? 說明:通過擴(kuò)展JSF提供的Converter接口,我們可以定義自己的轉(zhuǎn)換器。而為了能在頁面上使用自定義的轉(zhuǎn)換器,我們需要在配置文件中,對轉(zhuǎn)換器進(jìn)行注冊。
? ? ? ? ? ? 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();
}
}
頁面文件:
<%@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)頁面數(shù)據(jù)轉(zhuǎn)換為后臺數(shù)據(jù)時,Converter的getAsObject()方法會被調(diào)用。而當(dāng)需要把后臺數(shù)據(jù)顯示到頁面中時,getAsString()方法會被調(diào)用。這里,我們注意到,我們的Converter是不能帶任何配置屬性的,為了讓我們的Converter能夠在頁面中增加屬性,我們需要進(jìn)一步的定義自己的標(biāo)簽。除了可以根據(jù)converter-id來注冊轉(zhuǎn)換器以外,我們還可以使用converter-for-class標(biāo)簽來為某一特定的Java類型注冊轉(zhuǎn)換器。如果在轉(zhuǎn)換過程中發(fā)生錯誤的話,那么我們可以通過拋出ConverterException來停止后續(xù)的工作。
d) ? ? 驗證器定制:
? ? ? ? ? ? ? i. ? ? ? ? 說明:通過擴(kuò)展Validator接口,我們可以定義自己的驗證器。而為了能在頁面上使用自定義的驗證器,我們需要在配置文件中,對驗證器進(jìn)行注冊。
? ? ? ? ? ? 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"));
? ? }
? }
}
頁面文件:
<%@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>
注:定制驗證器的操作與定制轉(zhuǎn)換器的操作大致相同,如果我們希望通過頁面的方式為驗證器提供屬性的話,那么我們也需要定義額外的標(biāo)簽。
e) ? ? 控件定制:
? ? ? ? ? ? ? i. ? ? ? ? 說明:JSF允許用戶基于JSF的框架,進(jìn)行控件的定制,控件定制需要完成以下幾部分的工作,1.需要一個控件實現(xiàn)類,為了能讓控件在頁面上使用,我們的控件還需要在配置文件中,通過component標(biāo)簽進(jìn)行注冊;2.用于控件渲染的Render類;3.用于在頁面上使用控件的標(biāo)簽。
f) ? ? 控件樹定制:
? ? ? ? ? ? ? i. ? ? ? ? 說明:除了可以通過頁面標(biāo)簽來聲明控件以外,我們可以通過程序的方式來定制控件樹,通過調(diào)用FacesContext上的getViewRoot()方法,我們可以獲得控件樹的根引用,通過該引用,我們可以動態(tài)的對控件進(jìn)行增刪,或者動態(tài)的為某一控件增刪事件監(jiān)聽器,和驗證器等。
g) ? ? Web常量:
? ? ? ? ? ? ? i. ? ? ? ? 說明:JSF提供了一組操作application,context,session,cookies,page等常量的方法。通過調(diào)用FacesContext上的getExternalContext()方法,我們可以獲得一個對ExternalContext的引用,通過該引用我們可以獲取所有我們希望使用的session,cookies等常量的引用,除此以外,我們還能獲取request當(dāng)中的頭信息和請求信息等。