JSF學習筆記
JSF事件驅動型的MVC框架,與流行的struts比較學習,易于理解。jsf component event事件是指從瀏覽器由用戶操作觸發(fā)的事件,Struts application event 是用Action來接受瀏覽器表單提交的事件,一個表單只能對應一個事件,application event和component event相比是一種粗粒度的事件。優(yōu)點:事件粒度細化,方便實現。
JSF配置文件
一 web.xml
所有的請求都透過FacesServlet來處理,通過web.xml啟用jsf服務
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>
javax.faces.webapp.FacesServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
</servlet>
JSF預設會讀取faces-config.xml的定義,如果想要自行設置定義檔的名稱,我們是在web.xml中提供javax.faces.CONFIG_FILES參數,例如:
<web-app>
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>/WEB-INF/beans.xml</param-value>
</context-param>
</web-app>
定義檔可以有多個,中間以 "," 區(qū)隔,例如:
/WEB-INF/navigation.xml,/WEB-INF/beans.xml
二 faces-config.xml配置bean定義
<managed-bean>
<managed-bean-name>user</managed-bean-name>
<managed-bean-class>
onlyfun.caterpillar.UserBean
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean-name>供JSF頁面JSF表示語言使用
<managed-bean-class>對應java類
<managed-bean-scope>設定Bean的存活范圍
類中獲取Bean對象
如果要在其它類別中取得Bean對象,則可以先取得javax.faces.context.FacesContext,它代表了JSF目前的執(zhí)行環(huán)境對象,接著嘗試取得javax.faces.el.ValueBinding對象,從中取得指定的Bean對象,例如:
FacesContext context = FacesContext.getCurrentInstance();
ValueBinding binding =
context.getApplication().createValueBinding("#{user}");
UserBean user = (UserBean) binding.getValue(context);
如果只是要嘗試取得Bean的某個屬性,則可以如下:
FacesContext context = FacesContext.getCurrentInstance();
ValueBinding binding =
context.getApplication().createValueBinding("#{user.name}");
String name = (String) binding.getValue(context);
設置屬性的初始值
如果有必要在啟始Bean時,自動設置屬性的初始值,則可以如下設定:
<managed-bean>
<managed-bean-name>user</managed-bean-name>
<managed-bean-class>
onlyfun.caterpillar.UserBean
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>name</property-name>
<value>caterpillar</value>
</managed-property>
<managed-property>
<property-name>password</property-name>
<value>123456</value>
</managed-property>
</managed-bean>
如果要設定屬性為 null 值,則可以使用<null-value/>標簽,例如:
<managed-property>
<property-name>name</property-name>
<null-value/>
</managed-property>
List或Map型態(tài)的屬性
如果您的Bean上有接受List或Map型態(tài)的屬性,則您也可以在組態(tài)檔案中直接設定這些屬性的值,一個例子如下:
<managed-bean>
<managed-bean-name>someBean</managed-bean-name>
<managed-bean-class>
onlyfun.caterpillar.SomeBean
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>someProperty</property-name>
<list-entries>
<value-class>java.lang.Integer</value-class>
<value>1</value>
<value>2</value>
<value>3</value>
</list-entries>
</managed-property>
</managed-bean>
這是一個設定接受List型態(tài)的屬性,我們使用<list-entries>卷標指定將設定一個List對象,其中<value-class>指定將存入List的型態(tài),而<value>指定其值,如果是基本型態(tài),則會嘗試使用指定的 <value-class>來作Wrapper類別。
設定Map的話,則是使用<map-entries>標簽,例如:
<managed-bean>
<managed-bean-name>someBean</managed-bean-name>
<managed-bean-class>
onlyfun.caterpillar.SomeBean
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>someProperty</property-name>
<map-entries>
<value-class>java.lang.Integer</value-class>
<map-entry>
<key>someKey1</key>
<value>100</value>
</map-entry>
<map-entry>
<key>someKey2</key>
<value>200</value>
</map-entry>
</map-entries>
</managed-property>
</managed-bean>
由于Map對象是以key-value對的方式來存入,所以我們在每一個<map-entry>中使用<key>與<value>標簽來分別指定。
您也可以直接像設定Bean一樣,設定一個List或Map對象,例如在JSF附的范例中,有這樣的設定:
<managed-bean>
<description>
Special expense item types
</description>
<managed-bean-name>specialTypes</managed-bean-name>
<managed-bean-class>
java.util.TreeMap
</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
<map-entries>
<value-class>java.lang.Integer</value-class>
<map-entry>
<key>Presentation Material</key>
<value>100</value>
</map-entry>
<map-entry>
<key>Software</key>
<value>101</value>
</map-entry>
<map-entry>
<key>Balloons</key>
<value>102</value>
</map-entry>
</map-entries>
三 faces-config.xml配置頁面流轉
<navigation-rule>
<from-view-id>/pages/index.jsp</from-view-id>
<navigation-case>
<from-action>#{user.verify}</from-action>
<from-outcome>success</from-outcome>
<to-view-id>/pages/welcome.jsp</to-view-id>
</navigation-case>
</navigation-rule>
JSF每一個視圖(View)都有一個獨特的識別(identifier),稱之為View ID,在JSF中的View ID是從Web應用程序的環(huán)境相對路徑開始計算,設定時都是以/作為開頭,如果您請求時的路徑是/pages/index.faces,則JSF會將擴展名改為/pages/index.jsp,以此作為view-id。
<from-view-id>是個選擇性的定義,它規(guī)定了來源頁面的條件,<navigation-case>中定義各種導覽條件,<from-outcome>定義當窗體結果符合的條件時,各自改導向哪一個目的頁面,目的頁面是在<to-view-id>中定義。
可以在<navigation-case>中加入<from-action>,進一步規(guī)范窗體結果必須根據哪一個動作方法(action method),當中是使用 JSF Expression Language 來設定
<from-view-id>可以沒有設定,表示來源網頁不作限制,您也可以使用 * 顯式的在定義檔中表明
四 faces-config.xml配置自定義組件
<component>
<component-type>MyComponentType</component-type>
<component-class>customcomponent1.StrRepeat</component-class>
</component>
1. 創(chuàng)建組件類
a) 創(chuàng)建一個Java類,命名為StrRepeat,放在customcomponent1包中,修改其聲明,使繼承UIComponentBase類;
代碼如程序清單所示
b) 定義text和number屬性及其getter和setter方法;
c) 覆蓋父類的encodeEnd()方法;[encodeBegin()、encodeChildren()和encodeEnd()]
public class StrRepeat extends UIComponentBase{
@Override
public String getFamily() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void encodeEnd(javax.faces.context.FacesContext context)
throws IOException {
ResponseWriter writer = context.getResponseWriter();
for(int i = 0; i < this.getNumber(); i++) {
writer.writeText(this.getText(), "text");
}
}
private String text;
public String getText() { return text; }
public void setText(String text) { this.text = text; }
private int number;
public int getNumber() { return number; }
public void setNumber(int number) { this.number = number; }
}
2. 創(chuàng)建標簽處理器類
a) 創(chuàng)建一個Java類,命名為StrRepeatTag,放在customcomponent1包中,修改其聲明,使繼承UIComponentELTag類;
b) 創(chuàng)建text屬性和number屬性,以及它們的getter和setter方法;
c) 實現getComponentType()方法,使返回字符串“MyComponentType”;
d) 實現getRendererType()方法,使返回null;
e) 覆蓋setProperties()方法。
代碼如程序清單所示。
public class StrRepeatTag extends UIComponentELTag{
@Override
public String getComponentType() {
return "MyComponentType";
}
@Override
public String getRendererType() {
return null;
//throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected void setProperties(UIComponent component) {
super.setProperties(component);
component.getAttributes().put("text", this.getText());
component.getAttributes().put("number", this.getNumber());
}
private String text;
public String getText() { return text; }
public void setText(String text) { this.text = text; }
private int number;
public int getNumber() { return number; }
public void setNumber(int number) { this.number = number; }
}
4. 創(chuàng)建標簽庫描述符
a) 在NetBeans中新建一個文件,“類別”選擇“Web”,“文件類型”選擇“標記庫描述符”;
b) NetBeans打開“新建標記庫描述符”對話框,在其中輸入文件名如mytld;
c) 在根元素<taglib>內添加一個<tag>元素;
d) 在<tag>元素內,用<name>元素定義標簽名,用<tag-class>元素定義標簽處理器類,用<attribute>元素定義標簽屬性。
代碼如圖所示
<tag>
<name>repeatText</name>
<tag-class>customcomponent1.StrRepeatTag</tag-class>
<attribute>
<name>text</name>
</attribute>
<attribute>
<name>number</name>
</attribute>
</tag>
5. 配置自定義UI組件
a) 打開faces-config.xml,并且切換到XML顯示格式;
b) 將程序清單 10所示的代碼添加到faces-config.xml中,置于根元素<faces-config>之下;
faces-config.xml的完整代碼如圖 13所示(折疊了managed-bean的定義),其中高亮部分為自定義UI組件的定義。
<component>
<component-type>MyComponentType</component-type>
<component-class>customcomponent1.StrRepeat</component-class>
</component>
6. 編寫JSF頁面
a) 打開NetBeans自動創(chuàng)建Page1頁面,并切換到JSP顯示方式;
b) 在<jsp:root>標簽內加入前綴x的定義xmlns:x="/WEB-INF/tlds/mytld"
c) 在<webuijsf:form>標簽之下加入<x:repeatText text="asdadf" number="3"/>
五 JSF的國際化
資源文件的名稱是.properties,而內容是名稱與值的配對,資源文件名稱由basename加上語言與地區(qū)來組成,例如:
basename.properties、basename_en.properties、basename_zh_TW.properties
沒有指定語言與地區(qū)的basename是預設的資源檔名稱,JSF會根據瀏覽器送來的Accept-Language header中的內容來決定該使用哪一個資源檔名稱,例如:
Accept-Language: zh_TW, en-US, en
如果找不到對應的訊息資源文件,則會使用預設的訊息資源文件。
使用<f:loadBundle>卷標來指定加載訊息資源,一個例子如下:
<f:loadBundle basename="messages" var="msgs"/>
<h:outputText value="#{msgs.titleText}"/>
如果您的瀏覽器預設接受zh_TW語系的話,則頁面上就可以顯示中文,否則預設將以英文顯示,也就是messages.properties的內容,為了能顯示多國語系,我們設定網頁編碼為UTF8。
<f:view>可以設定locale屬性,直接指定所要使用的語系,例如:
<f:view locale="zh_TW">
<f:loadBundle basename="messages" var="msgs"/>
直接指定以上的話,則會使用繁體中文來顯示,JSF會根據<f:loadBundle>的basename屬性加上
JSF標簽
一 JSF核心標簽
二 JSF表單標簽
詳細資料http://www.web-tag.net
JSF生命周期(FacesServlet代碼閱讀)
private FacesContextFactory facesContextFactory = null;
private Lifecycle lifecycle = null;
Init方法:
facesContextFactory =
(FacesContextFactory)FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
LifecycleFactory lifecycleFactory = (LifecycleFactory)FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
String lifecycleId ;
// First look in the servlet init-param set
if (null == (lifecycleId = servletConfig.getInitParameter(LIFECYCLE_ID_ATTR))) {
// If not found, look in the context-param set
lifecycleId = servletConfig.getServletContext().getInitParameter (LIFECYCLE_ID_ATTR);
}
if (lifecycleId == null) {
lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
}
lifecycle = lifecycleFactory.getLifecycle(lifecycleId);
service方法:
FacesContext context = facesContextFactory.getFacesContext (servletConfig.getServletContext(), request, response, lifecycle);
lifecycle.execute(context);
lifecycle.render(context);
Lifecycle類負責JSF請求處理的全過程,主要是通過執(zhí)行其中的execute方法和render方法實現的
lifecycle.execute方法
private Phase[] phases = {
null, // ANY_PHASE placeholder, not a real Phase
new RestoreViewPhase(),
new ApplyRequestValuesPhase(),
new ProcessValidationsPhase(),
new UpdateModelValuesPhase(),
new InvokeApplicationPhase(),
response
};
for (int i = 1, len = phases.length -1 ; i < len; i++) {
if (context.getRenderResponse() ||
context.getResponseComplete()) {
break;
}
phases[i].doPhase(context, this, listeners.listIterator());
}
而在LifeCycle的execute方法中,是用一個for循環(huán)順序執(zhí)行幾個Phase。在每一個Phase執(zhí)行完之后,都會檢查FaceContext對象中是否設置了停止后續(xù)處理直接呈現響應的標志(renderResponse)或者已經完成了響應無需后續(xù)處理也不需要經過呈現響應階段了(responseComplete),如果標志為true,那么就不再執(zhí)行后續(xù)Phase。
lifecycle.render方法
if (!context.getResponseComplete()) {
response.doPhase(context, this,listeners.listIterator());
}
在LifeCycle的render方法中,也會檢查FacesContext的responseComplete狀態(tài),如果為true,那么就不再執(zhí)行render Phase。于是我們此刻知道了在我們自己所寫的一些代碼或者JSF庫里面的一些代碼中,調用FacesContext的responseComplete方法和renderResponse得作用原理。
最后,可以看到對于每一個phase都調用了doPhase方法,同時把LifeCycle和FacesContext當做參數傳入了。值得注意的是,所謂的phaseListener,也傳入了phase的doPhase方法中,由此大約能夠想明白這個“階段監(jiān)聽器”的道理了。
JSF事件
一 分類
1 動作事件Action Event普通動作響應
command組件通過注冊actionListener均可出發(fā)此事件偵聽響應
2 即時事件Immediate Event立即處理,
不驗證/轉換/更新模型值(即bean不會保存屬性)立即觸發(fā),需要一個為被注冊的UI組件binding到后臺bean中,常用來做bean層面即時服務,以執(zhí)行action為主要目的。 input與command都有一個immediate屬性,只要將其設定為true,就可以直接響應actionListener事件。
3 值改變事件Value Change Event
直接設定JSF輸入元件的valueChangeListener屬性
4 階段事件Phase Event監(jiān)聽響應的JSF生命周期
JSF的運行大致分為6個階段,每個階段會觸發(fā)該事件
二 編碼
1 屬性方式
<h:commandLink
actionListener="#{bean.linkActivated}"
actionListener="#{bean.linkActivated}">
</h:commandLink>
對應類的處理方法
public void listen(ActionEvent e) {}
2 tag方式
<h:commandButton image="mountrushmore.jpg" action="#{rushmore.act}">
<f:actionListener type="com.corejsf.RushmoreListener"/>
</h:commandButton>
對應的處理類
public class ChangeLocaleBean implements ActionListener {
public void processAction(ActionEvent e) {
FacesContext context = FacesContext.getCurrentInstance();
Map requestParams = context.getExternalContext().getRequestParameterMap();
String locale = (String) requestParams.get("locale");
if ("english".equals(locale))
context.getViewRoot().setLocale(Locale.UK);
else if("german".equals(locale))
context.getViewRoot().setLocale(Locale.GERMANY);
}
}
3 phase事件
<faces-config>
<lifecycle>
<phase-listener>com.corejsf.PhaseTracker</phase-listener>
</lifecycle>
</faces-config>
對應的處理類
public class PhaseTracker implements PhaseListener {
public PhaseId getPhaseId() {}
public void beforePhase(PhaseEvent e) {}
public void afterPhase(PhaseEvent e) {}
}