JSF通過定製標籤與JSP整合。之前展示過的所有JSF標籤,<h:inputText>、<h:outputText>、<h:form>和<f:view>等,都是定製標籤。根據規範要求,JSF 實現必須通過提供訪問所有標準組件、轉譯器、驗證器和轉換器的定製標籤來支援JSP。這些標籤庫(包括在JSF JAR中)列於表3-6中。
表3-6 JSF定製標籤庫
URI |
名 稱 |
通用首碼 |
說 明 |
http://java.sun.com/jsf/core |
Core |
f |
包含獨立於特定轉譯器的標籤(如<f:view>、<validator>,等等) |
http://java.sun.com/jsf/html |
HTML |
h |
包含所有標準組件和HTML呈現包 |
這些庫中的所有標籤都以某種特定的方式命名和實現。這樣,基於JSP的應用就保證可以在不同的 JSF 實現之間移植。大多數IDE,包括在本書中提到的,都可用於JSP開發。
因為JSP是唯一的所有實現都要求的顯示技術,我們在本書中都是用它來作為樣本。如果你不使用JSP也沒關係,我們講述的大多數概念並未和某種顯示技術緊密相關,你可以在附錄A讀到有關使用其他顯示技術的資訊。
對大多數問題,在JSP中使用JSF僅是使用JSF定製標籤庫的問題。但是,也還有一些你需要知道的要點,比如JSP包含。
3.2.1 使用JSP包含
JSP的關鍵特徵之一是,能夠將來自於多個JSP頁面中的內容整合到一個頁面中。這通常用於完成包含頁首和頁尾之類的工作。JSP 支援兩類包含:動態和靜態。動態包含(通過<jsp:include>標籤或JSTL <c:import>標籤實現)在運行時訪問資源。這種情況下,控制被轉寄到所包含的JSP。從被包含的JSP返回的響應將與調用頁面發回的響應進行合并。修改了動態被包含頁面後,它們將在所有調用頁面中自動更新。
靜態包含在JSP轉換——即在頁面被轉換成Java 代碼並且被編譯時間,整合資源。原頁面的內容基本上是被拷貝到調用頁面中。對被包含頁面的修改不能在調用頁面時自動更新。因為它們已經有了自己的內容拷貝。它們必須是能夠被編輯的,以便它們在更新內容時被重新編譯(JSP 2.0的隱含包含,可以在web.xml中進行配置,其處理也類似於靜態包含)。
JSF支援兩種包含。對動態包含,有兩個要求:
l 被包含頁面必須封裝在JSF <f:subview> 核心標籤中。這個標籤可以位於被包含頁面中,也可以圍繞包含語句;
l 被包含頁面中的所有模板文本和非JSF標籤必須位於JSF <f:verbatim> 核心標籤之內。
所以,假定有下面的JSP頁面的代碼片斷:
而Foo.jsp可能是這樣:
可以看到,整個被包含頁面被封裝在<f:subview>標籤中,並且所有的非JSF 標籤和模板文本封裝在<f:verbatim> 標籤中。另外,也可以將<f:subview> 標籤移到第一個頁面,圍繞在<jsp:include> 標籤之外。
使用靜態包含要更簡單些。無特別的限制——甚至並不非要使用<f:subview> 標籤。
在上例中,我們展示了一個假定的定製標籤,<customtag:dothis>,它可能執行任何任務。這樣就強調一個重點:可以和其他JSP定製標籤一起使用 JSF。
3.2.2 與JSTL以及其他JSP定製標籤一起使用JSF
這些有關JSF定製標籤庫的討論都很不錯,但是如果你希望有自己的定製標籤,或者使用第三方的標籤又如何呢?而如果你正在使用JSP 標準標籤庫(JSTL),這是能夠優雅地完成剛剛所述的所有事情的一套標準標籤,又該如何?多數情況下,可以將它們與你的JSF標籤混合使用。Faces標籤可以嵌套在其他標籤中。而對於某些產品,如IBM的WebSphere Application Developer [IBM,WSAD],鼓勵使用這種方法,而其他產品,如Sun的 Java Creator Studio [Sun,Creator],則鼓勵使用純粹的JSF方法。Oracle的 JDeveloper [Oracle,JDeveloper],允許你混合使用,但是也鼓勵你使用純粹的JSF方式。
註解 無論何時將JSF標籤嵌套到非JSF標籤中,一定要為它指定一個組件標識符(見前一章關於組件標識符的更多資訊)。
因為JSTL是標準,而且很多人對它都很熟悉,我們將用它來示範與定製標籤一起使用JSF(如果要獲得JSTL的詳細資料,參考Shawn Bayern的一本非常棒的書JSTL in Action [Bayern])。從一個簡單的例子開始(如代碼清單3-2所示)。該樣本混合使用了一些JSTL 標籤和JSF 標籤。這段代碼同時匯入了JSF 標籤庫和核心JSTL 標籤庫。
代碼清單3-2 混合JSTL標籤和JSF標籤
在這個例子中,JSTL 和JSF 標籤都嵌套在JSF <f:view> 標籤中,而該標籤定義了JSF組件樹的開始。例子使用了JSF HtmlOutputText 組件(<h:outputText>)和JSTL <c:out> 標籤來顯示文本。JSTL <c:import> 標籤將系統的web.xml檔案包含到頁面中(實際上你一般是不可能想要與他人分享這個檔案的,因此不要在實際應用中這樣做)。因為web.xml 是一個XML 檔案,<c:import> 標籤被嵌套在<f:verbatim>標籤中,後者是JSF UIOutput 組件,其轉譯器轉義了XML以便它在HTML頁面中正常顯示。這個例子並沒做什麼大不了的事情,但是它示範了在同一個頁面中混合使用不同標籤的能力。
注意,我們嵌套了JSTL標籤到JSF <f:verbatim> 標籤中。通常,嵌套JSF標籤到其他標籤之中是很容易的。事實上,任何可以顯示其子組件的組件,比如HtmlDataTable 和 HtmlPanelGrid,都要求模板文本和被嵌套的標籤位於<f:verbatim>標籤中(<f:verbatim> 標籤在第4章討論)。
隨同JSF使用JSTL最大的好處在於,兩者使用相似的運算式語言來引用對象(這對於JSP 2.0的運算式語言也是如此)。這樣使你可以很容易地以一種直觀的方式在JSTL和JSF標籤之間共用資料。為了示範這個特點,我們來看另一個例子,可以使使用者在HtmlInputText 控制項中輸入數值,然後使用該數值通過JSTL <c:forEach> 標籤來重複顯示字串。代碼示於代碼清單3-3中。
代碼清單3-3 對同一個後台bean混合使用JSF 和JSTL 標籤
警告 如果和受管bean一起使用JSP或者 JSTL運算式,你需要確保被引用的bean已被建立,可以通過JSF運算式、Java代碼或者你自己的定製標籤來實現。這是因為這些老的運算式語言並不知道JSF的受管bean建立工具(參考3.3節關於建立受管bean的資訊)。
這段代碼引用了一個稱為exampleBean的JavaBean,它有個int類型的number 屬性。使用了HtmlInputText組件來基於使用者輸入更新bean的屬性值。使用者點擊Go!按鈕(HtmlCommandButton組件)時,number屬性被更新,且頁面被重新顯示。這樣,JSTL <c:forEach>標籤將使用<c:out>標籤重複顯示文本exampleBean.number 次。<c:forEach> 標籤僅在exampleBean.number大於0時執行;這是由JSTL <c:if> 標籤控制的。
你不能在一個迭代其自身body的標籤中使用JSF組件標籤,如JSTL <c:forEach>標籤之中使用JSF標籤。推薦方法是使用HtmlDataTable 組件或者其他組件來在資料集或者集合之上進行迭代。
這個例子中,並沒有JSF 組件嵌套在JSTL <c:if>標籤中。但是如果組件被顯示一次後,然後在頁面重新顯示時又被諸如<c:if>之類的條件標籤隱藏,會怎樣?在組件第一次被顯示時,它被添加到視圖中。第二次,如果<c:if>標籤沒有顯示這個組件,JSF將把它從視圖中刪除。這意味著某些輸入控制項將丟失其本地值,而你就不能再引用這些組件(通過用戶端標識符引用或者在代碼中引用)。我們來看另一個例子,如代碼清單3-4所示,它來自於代碼清單3-3的同一個頁面。
JSTL <c:if> 標籤在exampleBean.number 大於10時,將執行其body部分。如果body部分被執行,那麼所有嵌套的組件將被添加到視圖中,並顯示出來。反之,這些組件將被刪除(如果它們先前已被添加了)。這種情況3-4所示。而圖3-5顯示的是代碼清單3-3和3-4所用的頁面的輸出。
圖3-4 假設你通過JSTL條件標籤(或者其他定製標籤)來控制組件的可視性,如果某個組件未被顯示,它將被從視圖中刪除。這意味著組件也會忘記其本地值
代碼清單3-4 通過JSTL有條件地顯示JSF組件
你也可以將這些組件放在HtmlPanelGroup中並且設定其rendered 屬性等於同一個運算式,以達到與代碼清單3-4相同的效果。HtmlPanelGroup被用作多個組件的容器。如下例:
圖3-5 顯示於代碼清單3-3和3-4中的JSP頁面的輸出。頂部的輸入欄位(HtmlInputText組件)的值被關聯到exampleBean.number後台bean屬性,而該值被JSTL <c:forEach> 標籤用於重複顯示字串exampleBean.number次。在頁面的底部,如果exampleBean.number大於10,JSTL <c:if>標籤將使用JSF 組件顯示一個表單。否則,組件將不會被顯示,並且會被從視圖中刪除(並且輸入控制項也將丟失其值)
如果exampleBean.number大於10,面板變為可見。這時,如果組件沒被顯示也將不被刪除。這是個不使用JSTL而使用純粹的JSF的好例子。
提示 即使定製標籤,像JSTL提供的那些,可以提供很多功能,但如果你正在從頭開發(或者重構),應該首先看看是否可以使用標準JSF組件來實現所需要的功能。使用優秀的組件和良好設計的後台bean,可以避免在頁面中使用太多的JSTL 標籤。你可以使用JSF標準組件來隱藏和顯示整個面板,或者完成各種強大的任務。
下面是幾個隨JSTL國際化和格式化標籤一起使用JSF標籤的互通性約束:
l 不推薦使用<fmt:parseDate> 和 <fmt:parseNumber>標籤。應該使用帶有DateTime 或者 Number 轉換器的(都在第6章討論)HtmlInputText 組件(第5章討論);
l 不應該使用用來決定或者指定頁面的字元集編碼的<fmt:requestEncoding> 標籤。通常,JSF 將自動處理這些,而如果需要強制指定特殊的編碼,應該使用JSP 頁面指令:<%page contentType="[content-type];[charset]"%>;
l 也不應該使用<fmt:setLocale>標籤。因為它不支援JSF,這可能會導致JSTL標籤使用一個場所而JSF 組件使用另一個場所,這將是一場災難。相反,你應該使用JSF的國際化特徵(在第6章討論)。為了控制特定頁面的場所,使用UIViewRoot 組件的locale屬性,這將在第4章討論。JSF的國際化特徵支援JSF和JSTL。
結合使用JSF 和JSTL的功能將是十分強大的。你自己開發的或者從第三方獲得的定製標籤都應該能夠可以很好的與我們這些所述的JSF和JSTL標籤一起工作。通常,你應該儘可能的堅持使用JSF 標籤。