Posted on 2006-12-21 10:11
冰浪 閱讀(753)
評論(0) 編輯 收藏 所屬分類:
Frameworks
注:此文為轉(zhuǎn)載自dev2dev:http://dev2dev.bea.com.cn/bbs/thread.jspa?forumID=121&threadID=35942
最新翻譯的:
?????????????? JSF1.2技術(shù)的新特性總結(jié)
本文的主要內(nèi)容:
?
??? “Web Tier to Go With Java EE 5”系列文章中,“JSF1.2新特性”是第一篇。就像在文中表述的一樣,JSP技術(shù)的優(yōu)勢隨著JSF技術(shù)的發(fā)展得到了進一步的擴大,這種優(yōu)勢是通過統(tǒng)一EL表達式建立起來的。統(tǒng)一JSP的框架也是JSF1.2技術(shù)中眾多重要成績之一,并在JSR252規(guī)范下進行開發(fā)。除了這些變化為,JSF技術(shù)還貢獻了大量的重要的容易使用的特性。這篇文章簡要的描述一些重要的基本特性,大概有如下幾點:
* 統(tǒng)一JSP技術(shù)
? * 改進自定義信息的支持
* 提供狀態(tài)保存特性
* 能夠關(guān)閉自動生成組件客戶端ID
* 新的setPropertyActionListener標(biāo)簽
《JavaServer Faces規(guī)范》的前言部分提供了一組完成的特性描述。
統(tǒng)一JSP技術(shù):
????? JSP和JSF技術(shù)相對立的核心是它們之間的頁面生命周期的不同。在以前的文章中已經(jīng)介紹了JSP規(guī)范,JSP規(guī)范指出:頁面的元素被執(zhí)行顯示時,JSP支持單一的“渲染-響應(yīng)”生命周期, 然后立即渲染頁面。JSF的生命周期分成了多個階段,當(dāng)一個組件樹被創(chuàng)建完畢,組件的數(shù)據(jù)就會被處理,然后組件被渲染并顯示在頁面上。然而,一個JSF頁面并不需要在執(zhí)行后立即渲染。因此,在JSP頁面上使用的JSF組件被渲染時,有時候并不是按照正常的順序完成,而且會丟失組件的一些狀態(tài)或者其他的問題產(chǎn)生。
這么文章涉及到JSP2.1的新特性。這里會從一下3個方面詳細的介紹JSF技術(shù)的改進的地方:
* 首先JSF組件支持JSTL的forEach標(biāo)簽
* 改進tree組件創(chuàng)建方法和內(nèi)容組織
* 反對只使用JSF技術(shù)的EL,贊成使用統(tǒng)一的EL
?????? 首先JSF組件支持JSTL的ForEach標(biāo)簽就像在描述JSP技術(shù)新特性的文章中表述的一樣,Java EE web層中的統(tǒng)一EL的一個新特性就是:迭代標(biāo)簽,例如JSTL的forEach能夠在JSF組件中直接使用。例如,使用輸入組件構(gòu)建一個簡單的表格,可以通過下列代碼來實現(xiàn):
<table>
<tr>
<th>Item Name</th>
???????? <th>Item Price</th>
<th>Item Quantity</th>
</tr>
<c:forEach var="item" items="#{shoppingCart.items}">
<tr>
??????? <td><h:outputText value="#{item.name}" /></td>
??????? <td><h:outputText value="#{item.price}" /></td>
??????? <td><h:inputText value="#{item.quantity}" /></td>
</tr>
</c:forEach>
<h:commandButton value="update quantities" action="update" />
</table>
沒有統(tǒng)一使用EL, 這幾乎是不可能實現(xiàn)的。
改進tree組件創(chuàng)建方法和內(nèi)容組織
?????? 在Hans Bergsten的《article about the use of JavaServer Faces technology with JSP pages》文中寫到的,混合使用JSP代碼和JSF標(biāo)簽有時候會產(chǎn)生預(yù)期不到的結(jié)果。例如,在下面的代碼中,JSF 組件outputText輸出的文本內(nèi)容“Hi”本來應(yīng)該在文本內(nèi)容“what’s your name?”前面顯示:
<h:panelGroup>
????????? <h:outputText value="Hi. "/>
???????????? What's your name?
????? </h:panelGroup>
然而,這兩行代碼的輸出是順序是相反的。是什么導(dǎo)致了問題的發(fā)生?JSF的實現(xiàn)是以JSP的渲染引擎來渲染組件樹的。因為JSP會立即執(zhí)行并把靜態(tài)文本內(nèi)容放在response中,而<panelGroup>的子節(jié)點標(biāo)簽直到相應(yīng)的結(jié)束標(biāo)簽</panelGroup>匹配后才會被渲染,所以“what’s your name?”就會在“Hi”之前顯示了。
像這樣的問題在Hans Bergsten的文中已經(jīng)提到并在JSF1.2版本中得到了修改。解決方案包括:怎樣改變組件樹的創(chuàng)建、各種內(nèi)容的處理和渲染等等。最重要的改變?nèi)缦聨c:
* 創(chuàng)建和渲染組件樹被分成2步,為了防止JSP引擎過早的渲染頁面內(nèi)容。
* 所有的渲染器的rendersChildren屬性值現(xiàn)在已經(jīng)設(shè)計為“true”,目的時防止聯(lián)合組件的內(nèi)容在各自的組件內(nèi)部分別渲染。
* 標(biāo)簽內(nèi)部的靜態(tài)內(nèi)容對應(yīng)的渲染器的rendersChildren屬性值設(shè)置為true,目的是獲取并保存一個短暫的UIOutput組件并把該組件添加到組件樹中。
* 狀態(tài)管理器從ViewTag中移到了ViewHandler中,為了防止write State在實際保存過程中被調(diào)用。
?????? 在接下來的“Web Tier to Go With Java EE 5”系列文章中會提供更多的關(guān)于描述組件樹的創(chuàng)建和內(nèi)容組織的細節(jié),這就讓JSF標(biāo)簽組件中使用JSP代碼成為了可能。
反對只使用JSF的EL,贊成使用統(tǒng)一的EL
?????? 如本文前面的內(nèi)容介紹,JSP和JSF的EL表達式會整合成統(tǒng)一的EL。這就意味著現(xiàn)在的JSF的EL是不推薦使用的。在介紹統(tǒng)一的EL前已經(jīng)使用了JSF開發(fā)的應(yīng)用,請務(wù)必做好具有向后兼容的工作。
?????? 然而,為了利用統(tǒng)一EL的特性,你可能需要改變原來自定應(yīng)的組件和自定義的標(biāo)簽。要移植到統(tǒng)一EL,首先要把ValueBinding換成ValueExpression,把MethodBinding換成MethodException。這些改變都相對的容易,而且還有更好的辦法可以完成這些改變。例如,因為所有標(biāo)準(zhǔn)的標(biāo)記屬性都可以訪問值表達式,你的標(biāo)記處理程序不需要檢測屬性是可以接收、訪問一個值表達式。
????? 要了解更多關(guān)于如何移植到統(tǒng)一EL的內(nèi)容,請參見文章“Unified Expression Language”(http://java.sun.com/products/jsp/reference/techart/unifiedEL.html)
改進易用性來支持自定義信息
????? JSF1.2技術(shù)的一個重要改進就是在JSF應(yīng)用中增加自定義信息的能力。這個改進包括如下幾個方面:
* 一組新的標(biāo)準(zhǔn)轉(zhuǎn)換信息
* 對輸入組件提供了新的屬性:requiredMessage、converterMessage、validatorMessage
* 為輸入組件提供了新的label屬性,允許組件的名字中包含錯誤信息
* 提供新的資源綁定元素在應(yīng)用中綁定資源。
一組標(biāo)準(zhǔn)的轉(zhuǎn)換器信息
????? 以前的JSF給標(biāo)準(zhǔn)驗證器提供了一組標(biāo)準(zhǔn)的錯誤信息。版本1.2增加了標(biāo)注轉(zhuǎn)換器提供了一組錯誤信息。要查看這些信息,請參考JSF1.2規(guī)范(http://jcp.org/en/jsr/detail?id=252)的第2.5.2.4節(jié)。
新屬性:requiredMessage,converterMessage,validatorMessage
????? 很多情況下,標(biāo)準(zhǔn)的錯誤提示信息很適合為我們的需要。如果不合適,我們可以使用自定義的信息來覆蓋這些標(biāo)準(zhǔn)的信息,要完成個目的,就是通過輸入組件的新屬性requiredMessage、converterMessage、validatorMessage來完成。
????? 當(dāng)你需要修改信息時,新的特性允許你覆蓋原有的信息。例如,假設(shè)你在userid和password2個域中使用了驗證長度的驗證器。你希望一個錯誤信息提示“Userid必須時9個字符”,另一個“Password必須是9個字符”,但是你希望對這2個輸入域使用同一個驗證器。通過validatorMessage屬性,你可以在適當(dāng)?shù)慕M件上設(shè)置特殊的信息,就不用為每個驗證器實例提供單獨的提示信息。
????? 這些屬性接收字符型的值以及值表達式,和JSF的標(biāo)記屬性一樣的工作原理。因此,你可以使用值表達式來引用綁定資源中的信息,如下面的例子:
<h:inputText value="#{customer.userID}"
????????????? validatorMessage="#{customMessages.userIdMessage}" >
???????????? <f:validateLongRange minimum="9" maximum="9"/>
???? </h:inputText>
????? 就像你猜想的一樣,輸入組件的屬性requiredMessage值覆蓋了默認(rèn)的信息提示。輸入組件的屬性converterMessage值覆蓋了默認(rèn)的轉(zhuǎn)換信息提示。同樣的,輸入組件的屬性validatorMessage值也覆蓋了默認(rèn)的驗證信息提示。
????? 因為這種特性,頁面開發(fā)人員現(xiàn)在可以更為合適的提供錯誤的提示信息。這樣,用戶能夠更準(zhǔn)確的直到錯誤的根源。
輸入組件的新屬性:label
????? 輸入組件的label新屬性是另外一個幫助用戶確認(rèn)錯誤信息的重要特性。許多由組件產(chǎn)生的標(biāo)準(zhǔn)錯誤信息包括根據(jù)參數(shù)得到的信息。輸入組件的label屬性值是用來適當(dāng)取代相關(guān)的默認(rèn)信息的。例如,下面的信息是與DateTimeConverter相關(guān)的信息,用DATE_ID標(biāo)識:
????? {2}: "{0}" could not be understood as a date.
這就是說,你有一個DateTimeConverter實例,并與一個文本輸入框綁定。設(shè)置輸入組件的label屬性為BirthDate。如果用戶在文本輸入框中輸入“older than the hills”,轉(zhuǎn)換器就失敗了。當(dāng)頁面再次被渲染的時候,用戶就能看到如下的信息提示:
????? Birth Date: "older than the hills" could not be understood as a date.
?????
???? Label組件接受文本內(nèi)容,也接受值表達式。所以,在使用其他的JSF標(biāo)簽屬性時,你可以使用表達式來引用綁定的資源文件內(nèi)容。
新的資源配置元素
????? JSF1.2版本以前的規(guī)范,頁面開發(fā)人員使用loadBundle標(biāo)簽來引用資源文件,從而達到本地化的目的。在新的1.2版本中,頁面開發(fā)人員一樣可以在頁面中使用資源綁定。
更高效率的裝載綁定好的資源,包括在應(yīng)用系統(tǒng)的配置文件中使用資源綁定元素進行注冊資源的綁定,新的規(guī)范提供了一個很好的入口方式。下面的資源綁定元素注冊一個叫做“CustomMessages的ResourceBundle類,這個類存在應(yīng)用系統(tǒng)的資源包里:
<resource-bundle>
???????? <var>customMessages</var>
???????? <base-name>resources.CustomMessages</base-name>
???? </resource-bundle>
var子元素定義了資源引用名稱,頁面開發(fā)人員在開發(fā)頁面時可以采用如下的方式進行調(diào)用:
<h:outputText value="#{customMessages.myText}" />
????? 在這個例子中,myText就是定義在資源文件中,我們要引用的信息的鍵(key)。
通過這個新的資源綁定元素,我們可以在應(yīng)用系統(tǒng)中綁定更多的資源文件。這樣做,新的元素的使用,不僅消除了在多個頁面使用“l(fā)oadBundle”綁定資源的方式,而且大大提高了性能,因為裝載資源是很“昂貴”的操作。
提供狀態(tài)保存特性
?????? JSF1.2版本在狀態(tài)管理方面主要有2個變化。一個變化是,當(dāng)應(yīng)用系統(tǒng)使用多個框架(frame)或者窗口(window)時,整合系統(tǒng)狀態(tài)的不穩(wěn)定問題;另一個變化是提供了客戶端安全狀態(tài)操作。
????? 在多框架或者多窗口的應(yīng)用中系統(tǒng)狀態(tài)不穩(wěn)定的原因是這些復(fù)雜邏輯試圖會把根視圖的ID重復(fù)復(fù)制,因此狀態(tài)管理就變得混亂了。在1.2版本中做了一些修改,提供了一些狀態(tài)管理的API來解決這個問題。
????? 首先,在視圖中的每一個窗口(Window)或者框架(Frame)都有唯一的ID標(biāo)識,這個ID標(biāo)識是視圖根ID和一個隨即數(shù)據(jù)聯(lián)合組成,并保存在一個隱藏域中,用來連接窗口(Window)或者框架(Frame)。ViewHandler中的writeState方法已經(jīng)被修改,并用來生成唯一的ID,并且在JSF的“渲染-回應(yīng)”階段輸出到客戶端。另外,encodeEnd方法中,UIForm也被修改,讓它在輸出“閉合標(biāo)簽”到客戶端的這個過程之前調(diào)用writeState方法,以便多表單(form)的狀態(tài)被保存下來。
????? 同時,在物理“渲染-回應(yīng)“階段,StateManager提供了最新的saveSerializedView方法會使用這個唯一的ID把對應(yīng)的視圖序列化到Session中。例如:如果服務(wù)器崩潰了,這些被保存的狀態(tài)就能在另一個服務(wù)器中再次使用。所以,“狀態(tài)-保存”機制在JSF1.2版本在中被高度有效的支持。最后,ViewHandler類中的restoreView方法可以使用唯一的ID來標(biāo)識恢復(fù)視圖,即使是在“恢復(fù)視圖”階段。
???? 另一個重要的規(guī)定,就是允許對客戶端進行加密,包括在發(fā)送一個關(guān)閉標(biāo)簽到客戶端之前。你可以選擇聲明的方式對客戶端進行加密:使用ClientStateSavingPassword這個類作為加密方式的入口,如下所示:
<env-entry>
???????????? <env-entry-name>
???????????????????? com.sun.faces.ClientStateSavingPassword
???????????? </env-entry-name>
???????????? <env-entry-value>somePassword</env-entry-value>
???????????? <env-entry-type>java.lang.String</env-entry-type>
???? </env-entry>
?????? 你提供的密碼通常是生成 值/對的方式進行加密。如果這個環(huán)境入口沒有在配置文件中定義,這個加密方法就不會被執(zhí)行。
關(guān)閉自動生成客戶端組件ID的功能
????? JSF采用一種算法來生成客戶端組件(包含在Form中)的表示號(ID),客戶端組件的ID如下面的形式:
?????????? [form ID]:[clientID]
????? 頁面開發(fā)人員可以對指定ID的form組件或者包含在該form中的其他組件進行操作。如果頁面開發(fā)人員沒有指定form的ID號或者組件的ID號,那么系統(tǒng)會自動生成。
????? 早期版本的JSF規(guī)范沒有規(guī)定這個算法,只要開發(fā)人員理解其工作原理就夠了。此外,對于自定義的組件或者一個渲染類來說,頁面開發(fā)人員沒辦法確定是否已經(jīng)設(shè)置了組件的客戶端ID號或者是否系統(tǒng)為它們自動生成。最后,因為某種原因,渲染器的開發(fā)人員可能無法改變它的名稱,但是仍然要在JavaScript中引用它。
????? 最后的JSF規(guī)范明確的說明了生成客戶端組件ID的算法。我們可以在UIComponent的API文檔中找到相關(guān)信息。由于UIForm組件增加了prependId屬性,相應(yīng)的算法也做了變化。頁面開發(fā)人員現(xiàn)在可以改變該屬性的默認(rèn)值(默認(rèn)值為false,可以改為true),這樣就表明可以不需要使用Form的ID作為組件ID的前綴了。下表格顯示了正常情況下生成客戶端組件ID的不同點:
Table 1:客戶端組件ID生成情況
場景 ??????????????????????????????????? prependId=rue prependID=alse
開發(fā)人員沒有設(shè)置ID ??????????????????? _id0:_id1 ???????? _id1
開發(fā)人員僅僅設(shè)置了form的ID myForm:_id0 _id0
開發(fā)人員只設(shè)置組件ID ???????? _id0:myComponent myComponent
開發(fā)人員對form和組件都設(shè)置了ID myForm:myComponent myComponent
新標(biāo)簽setPropertyActionListener
????? 除了actionListener標(biāo)簽之外,允許我們給自定義的組件注冊一個Action Listener,核心的標(biāo)記庫現(xiàn)在已經(jīng)包括在setPropertyActionListener標(biāo)記中。用這個標(biāo)記注冊一個特定的action listener到ActionSource實例中,并與具體的組件關(guān)聯(lián)起來。當(dāng)組件被激活,listener就會把對象的引用存儲到標(biāo)記的屬性中。
????? 為了展示標(biāo)記的用處,假設(shè)我們有一個forEach標(biāo)記,并且在其中使用了commandButton,以及使用iterator來顯示一組書的信息,代碼如下:
<c:forEach items="#{bookDBAO.books}" var="book" varStatus="stat">
????????? <c:set var="book" scope="request" value="${book}"/>
????????? ...
????????? <h:commandButton id="add" action="#{catalog.add}"
???????????????? value="#{bundle.CartAdd}">???
???????????????? <f:setPropertyActionListener
??????????????????????? target="#{requestScope.book}"
??????????????????????? value="#{book}"/>
????????? </h:commandButton>
????????? <c:remove var="book" scope="request"/>
???? </c:forEach>
當(dāng)用戶點擊button,選擇的book就會增加到購物車中,并且顯示下一頁。
如果你熟悉JSTL,那你就直到forEach的var屬性是在本頁面那有效的。然而,book的數(shù)據(jù)需要在request范圍內(nèi)有效,這樣顯示頁面(點擊后轉(zhuǎn)到的下一頁面)才能范問到book的數(shù)據(jù)。因此,setPropertyActionListener標(biāo)簽通常用來把當(dāng)前(book)對象設(shè)置為request訪問范圍內(nèi)。
?????? 在這個例子中,setPropertyActionListener標(biāo)簽的value屬性指的是“book”對象;target屬性是表達式“requestScope.book”的引用,并且和commandButton組件建立了關(guān)聯(lián)。
總結(jié)
????? 這篇文章只是JSF1.2規(guī)范的簡要說明,介紹一些新的特性和常見問題的解決方法。我們建議大家使用一下JSF1.2。如果你有一些問題或者看法,請發(fā)郵件(users@javaserverfaces.dev.java.net)給我們或者加入我們的社區(qū)(http://java.sun.com/j2ee/javaserverfaces)。同時,期待下一篇文章在我們的網(wǎng)站上出現(xiàn)。