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