世事如棋
          Aspire to Professionalism
          posts - 4,  comments - 12,  trackbacks - 0
          作者:Andrei Cioroianu
          轉(zhuǎn)自:Oracle Technology Network http://www.oracle.com/technology/global/cn/pub/articles/andrei_reuse.html

          了解如何利用 JSP 標(biāo)記文件、JSF 和 Oracle ADF Faces 重用 Web 內(nèi)容和 Java 代碼。

          本文相關(guān)下載:
          示例代碼
          Oracle JDeveloper 10g (10.1.3)

          2005 年 10 月發(fā)表

          代碼重用是提高開發(fā)人員生產(chǎn)效率和應(yīng)用程序可維護(hù)性的一種非常好的方式。您應(yīng)當(dāng)總是尋找設(shè)計良好的框架和可自定義的組件,而不是從頭重來。應(yīng)用程序特有的代碼也可以在模塊甚至相關(guān)項目間重用。后一種可重用性可使您快速修改,整體利用新特性,并減少測試和調(diào)試的時間。

          雖然這些聽起來像是針對程序員的不錯建議,但 Web 開發(fā)人員也應(yīng)當(dāng)注意這些事情。許多 Web 開發(fā)人員已經(jīng)在使用諸如 Java Server Faces (JSF)、Oracle ADF Faces 和 Apache MyFaces 之類的框架,這些框架提供了許多內(nèi)置組件并支持創(chuàng)建其他可重用組件。然而,很多時候是將許多 HTML 和 JSP 標(biāo)記從一個 Web 頁面復(fù)制粘貼到其他頁面中,這意味著當(dāng) Web 內(nèi)容改變時將不得不修改這些頁面中的重復(fù)標(biāo)記。此外,如果沒有更新某些頁面,那么應(yīng)用程序的外觀將會不一致。如果跨頁面重用 UI 組件就不會發(fā)生這種情況了,這是因為發(fā)生變化時只需在一個地方進(jìn)行編輯就可以了。

          在本文中,我將提供一些在基于 JSF 和 ADF Faces 的 Web 應(yīng)用程序中重用 UI 組件的最佳實踐。您將了解到如何創(chuàng)建定義 Web 頁面布局的模板,以及如何重用表單、菜單和按鈕欄。您還將了解到如何轉(zhuǎn)換現(xiàn)有的 JSP 頁面以使它們更易于維護(hù),以及如何將由 JSF 和 Oracle ADF Faces 提供的組件與 JSTL 和現(xiàn)代 JSP 特性(例如標(biāo)記文件)一起使用。

          Java Web 可重用特性

          自從第一個版本起,JSP 就已經(jīng)提供了一些鼓勵可重用的基本機(jī)制,例如 JavaBeans 支持、基于 Servlets API RequestDispatcher 的 <%@include%> 指令和 <jsp:include> 標(biāo)記。JSTL 增加了 <c:import> 標(biāo)記,它使您能夠包含某個資源的內(nèi)容,該資源可以位于同一個應(yīng)用程序、服務(wù)器中,甚至也可以在遠(yuǎn)程服務(wù)器上。Struts Tiles 圍繞著這種內(nèi)容包含特性構(gòu)建了一個完整的框架。JSF 也支持這一特性,允許您構(gòu)建使用 <f:subview> 標(biāo)記的子表單。JSP 2.0 增加了一個稱為“隱式包含”的新特性。這些特性使用 <include-prelude><include-coda> 在 web.xml 文件中聲明。正如您所能看到的,雖然頁面/片斷包含種類各異,但每一種都有其自己的用途和上下文。

          對自定義標(biāo)記的支持從 JSP 1.1 就有了,它為構(gòu)建標(biāo)記庫提供了一個 API。JSP 1.2 對該 API 進(jìn)行了增強(qiáng),但很多人認(rèn)為它太復(fù)雜了。因此,JSP 2.0 定義了一個具有相同功能的全新 API。這個為標(biāo)記庫提供的新 API 稱為簡單標(biāo)記 API,舊 API 現(xiàn)在稱為標(biāo)準(zhǔn)標(biāo)記 API。許多 Web 框架(如 Struts、JSF 和 JSTL)仍使用標(biāo)準(zhǔn)標(biāo)記 API,以便可以與 JSP 1.2 以及 JSP 2.0 一起使用。簡單標(biāo)記 API 是另一種 JSP 2.0 特性 — 標(biāo)記文件 — 的基礎(chǔ),該特性使您能夠使用 JSP 語法構(gòu)建標(biāo)記庫。除了簡單標(biāo)記和標(biāo)記文件之外,JSP 2.0 規(guī)范還定義了 EL 函數(shù),后者使您能夠使用 EL 語法從 JSP 頁面中調(diào)用靜態(tài) Java 方法。

          JSF 標(biāo)準(zhǔn)將組件定義為它的可重用單元。這些組件比自定義標(biāo)記更強(qiáng)大,但也更難設(shè)計和實施。因為有幾個公司和開放源代碼機(jī)構(gòu)正在制作可供使用的 JSF 組件庫,所以您可能不需要構(gòu)建自己的 JSF 組件。本文的示例使用了 Oracle ADF Faces,它是基于 JSF 標(biāo)準(zhǔn)的最先進(jìn)的框架。

          創(chuàng)建頁面模板。典型 Web 應(yīng)用程序的所有頁面共享一個公共布局,該布局可以定義在一個地方,如 JSP 標(biāo)記文件中。該模板可以生成標(biāo)題和正文標(biāo)記、應(yīng)用程序的菜單以及在所有頁面中出現(xiàn)的其他部分。此外,它可以包含用于加載資源綁定、設(shè)置 JSP 變量等的設(shè)置標(biāo)記。在應(yīng)用程序的每個 Web 頁面中重復(fù)該標(biāo)記是沒有意義的。在這一部分中,您將了解如何使用 Oracle JDeveloper 10g (10.1.3)(撰寫此文時為早期試用版)基于 JSF 和 Oracle ADF Faces 構(gòu)建自定義模板。

          JDeveloper 提供了一個創(chuàng)建 JSF 頁面模板的向?qū)А?File 菜單中選擇 New 項,打開 New Gallery 窗口。然后,轉(zhuǎn)至 Web Tier 中的 JSF 類別,在右側(cè)面板中選擇 JSF JSP Template 并單擊 OK:

          圖 1

          單擊 Next 跳過 Welcome 頁面,然后選擇您使用的 J2EE 版本,并再次單擊 Next:

          圖 2

          為模板提供一個文件名:

          圖 3

          選擇組件綁定樣式:

          圖 4

          指定是否要使用錯誤頁面:

          圖 5

          選擇要使用的標(biāo)記庫:

          圖 6

          提供頁面標(biāo)題和其他頁面屬性:

          圖 7

          單擊 Finish。JDeveloper 將創(chuàng)建該模板并在可視化編輯器中將其打開。您可以使用 Component Palette 將 JSF 和 Oracle ADF Faces 組件添加到該模板中。然后,您可以在 New Gallery 窗口中從 Template 中選擇 JSF JSP,基于您剛創(chuàng)建的模板創(chuàng)建 JSF 頁面。這是從模板構(gòu)建頁面的一種非常簡單的方法。另一種方法是將該共用的 JSF 標(biāo)記移到一個可重用的標(biāo)記文件中。以下段落使用了第二種方法。

          創(chuàng)建標(biāo)記文件。從 File 菜單中選擇 New 項,打開 New Gallery 窗口。然后,轉(zhuǎn)至 Web Tier 中的 JSP 類別,在右側(cè)面板中選擇 JSP Tag File 并單擊 OK:

          圖 8

          JDeveloper 將打開一個創(chuàng)建 JSP 標(biāo)記文件的向?qū)Т翱?。單?Next 跳過 Welcome 頁面,在 File Name 域中輸入 pageTemplate.tag 并單擊 Next:

          圖 9

          現(xiàn)在您就可以定義模板標(biāo)記的屬性了。假定您正在構(gòu)建一個基于 Web 的向?qū)?,您希望每個頁面都有一個步驟 ID 和一個標(biāo)題。標(biāo)記文件需要該信息來為每個頁面自定義模板標(biāo)記。單擊 Add 按鈕,輸入 step 屬性名,并將 Required 設(shè)為 true。對另一個名稱為 title 的屬性執(zhí)行同樣的操作:

          圖 10

          單擊 Next 和 Finish。JDeveloper 將在 WEB-INF 目錄的 tags 子目錄下創(chuàng)建 pageTemplate.tag 文件。用 <%@attribute%> 指令定義這兩個標(biāo)記屬性:
          <%@ attribute name="step" required="true" %>
          <%@ attribute name="title" required="true" %>
          
          在創(chuàng)建標(biāo)記文件之后,JDeveloper 將打開它進(jìn)行編輯。以下段落介紹了本文通篇用到的示例應(yīng)用程序的模板代碼。

          在標(biāo)記文件中使用 JSF 和 Oracle ADF Faces。無論您是否想在普通頁面的標(biāo)記文件內(nèi)使用這些框架,您都必須用 <%@taglib%> 指令來對其進(jìn)行聲明。在 Component Palette 中選擇 JSP,然后單擊 Taglib。您需要在一個對話框中輸入要使用的標(biāo)記庫的 URI 和前綴:

          圖 11

          您還可以使用 Component Palette 來將任何標(biāo)記拖放到 JSP 頁面上,如果它的 <%@taglib%> 指令不在頁面中,那么 JDeveloper 將自動添加它。pageTemplate.tag 示例使用了四個標(biāo)記庫:JSTL Core、JSF Core、Oracle ADF Faces Core 和 Oracle ADF Faces HTML:
          <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
          <%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
          <%@ taglib prefix="af" 
          uri="http://xmlns.oracle.com/adf/faces" %>
          <%@ taglib prefix="afh" 
          uri="http://xmlns.oracle.com/adf/faces/html" %>
          
          在同時使用 JSF 和 JSTL 時,或者在標(biāo)記文件中使用 JSF 時有一件非常重要的事情您必須始終牢記:JSF 框架不支持頁面范圍,后者是 JSTL 標(biāo)記的默認(rèn) JSP 范圍。因此,您應(yīng)當(dāng)顯式指定與 JSTL、JSF 和 Oracle ADF Faces 的標(biāo)記結(jié)合使用的 JSP 變量的請求范圍。此外,標(biāo)記文件的所有屬性都可以通過保存在頁面范圍內(nèi)的 JSP 變量來訪問。您必須復(fù)制請求范圍內(nèi)的屬性,以便它們可以和 JSF 和 Oracle ADF Faces 標(biāo)記一起使用:
          <c:set var="pageTitle" scope="request" value="${pageScope.title}"/>
          
          您還可以將屬性的值存儲在由 JSF 管理的 Bean 內(nèi)。假定您有一個名稱為 WizardBean 的類,該類在 faces-config.xml 中被配置為受管 Bean:
          <faces-config>
              ...
          <managed-bean>
          <managed-bean-name>wizard</managed-bean-name>
          <managed-bean-class>webreuse.WizardBean</managed-bean-class>
          <managed-bean-scope>session</managed-bean-scope>
          </managed-bean>
              ...
          </faces-config>
          
          示例 Bean 有一個 currentStep 屬性,您可以在該屬性中存儲 step 屬性的值。該操作可以利用 JSTL 的 <c:set> 標(biāo)記來完成,但您必須確保該 Bean 存在于會話范圍中(如 faces-config.xml 文件中所聲明的那樣)。只有在 JSF EL 表達(dá)式中首次引用受管 Bean 實例時,JSF 才會創(chuàng)建該實例。如果在訪問受管 Bean 的 JSF 或 Oracle ADF Faces 標(biāo)記之前執(zhí)行 JSTL 標(biāo)記,那么,您應(yīng)當(dāng)使用 <jsp:useBean>以確保該 Bean 實例存在于會話范圍中。如此,您就可以安全地使用 <c:set> 標(biāo)記了:
          <jsp:useBean class="webreuse.WizardBean" 
          id="wizard" scope="session"/>
          <c:set target="${wizard}" property="currentStep" 
          value="${pageScope.step}"/>
          
          以上代碼可以手動輸入,或者可以使用 JDeveloper 的 Component Palette。選擇 JSP,然后單擊 UseBean,添加 <jsp:useBean> 標(biāo)記。JDeveloper 將打開一個對話框,您必須在其中提供該標(biāo)記的屬性:

          圖 12

          最后,您可以在 pageTemplate.tag 文件中添加模板代碼:
          <f:view>
          <afh:html>
          <afh:head title="#{pageTitle}"/>
          <afh:body>
          <af:panelPage title="#{pageTitle}">
          <jsp:doBody/>
          </af:panelPage>
          </afh:body>
          </afh:html>
          </f:view>
          
          所有的 JSF 和 Oracle ADF Faces 組件都必須置于 <f:view> 內(nèi)部。<afh:html>、<afh:head><afh:body> 組件將生成具有相同名稱的 HTML 標(biāo)記。<af:panelPage> 組件顯示頁面標(biāo)題。然后,<jsp:doBody> 將調(diào)用您放在使用模板標(biāo)記的 JSP 頁面中的 <tags:pageTemplate></tags:pageTemplate> 之間的 JSP 內(nèi)容。這種 JSP 頁面將類似于:
          <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
          
          <tags:pageTemplate step="..." title="...">
          ... JSP content executed by doBody ...
          </tags:pageTemplate>
          
          當(dāng)執(zhí)行該頁面時,標(biāo)記文件將生成 <html><head><body> 標(biāo)記以及頁面標(biāo)題和其他標(biāo)題標(biāo)記。然后,模板標(biāo)記文件將調(diào)用包在 <tags:pageTemplate></tags:pageTemplate> 之間的 JSP 內(nèi)容。在此之后,標(biāo)記文件可能會生成一個頁腳,并用 </body></html> 來完成頁面。下一部分將在基于 Web 的向?qū)У捻撁嬷惺褂媚0鍢?biāo)記。

          重用表單、菜單和其他 UI 組件

          利用 JSF 和 Oracle ADF Faces 組件構(gòu)建的 UI 面板可以使用 "subviews" 或 JSP 標(biāo)記文件在多個頁面中重用。前一種解決方案與舊的 JSP 1.2 版本兼容,但標(biāo)記文件更靈活。這一部分將介紹如何基于 JSF 和 Oracle ADF Faces 將一個 Web 表單分成多個頁面和標(biāo)記文件。當(dāng)用戶第一次訪問應(yīng)用程序時,他將使用包含后退和前進(jìn)按鈕的向?qū)浇缑鎭碇鸩綖g覽這些頁面。在此以后,他可能想更新最初提供的信息。在這種情況下,用戶需要使用基于菜單的界面直接訪問應(yīng)用程序的頁面。

          可以在上述的兩種情況下使用同一種 Web 表單。此外,所有的表單可以組合在一個確認(rèn)頁面中,該頁面可以讓用戶以只讀模式查看信息。當(dāng)用戶必須修改其中一個表單時,可以編輯單個文件。每一個修改都將顯示在向?qū)эL(fēng)格的界面(用于從用戶那獲取信息)、確認(rèn)頁面(用于查看信息)和基于菜單的界面(由用戶用于更新信息)中。如果不重用表單,您將必須在三個不同的地方進(jìn)行相同的修改。

          開發(fā) Backing Bean。您在前一部分中已經(jīng)發(fā)現(xiàn),示例應(yīng)用程序使用了一個名稱為 WizardBean 的 Backing Bean。pageTemplate.tag 文件設(shè)置了 currentStep 屬性,該屬性保存用戶在瀏覽器中看到的當(dāng)前表單的步驟 ID。示例應(yīng)用程序在確認(rèn)頁面(第四步)之前使用了三個表單,確認(rèn)頁面的 ID 由 MAX_STEP 常量來定義。將該常量公開為名為 maxStep 的 bean 屬性,以便可以在 Web 頁面中使用 JSF EL 來訪問它:
          package webreuse;
          
          public class WizardBean implements java.io.Serializable {
          public final static int MAX_STEP = 4;
          private int currentStep;
          private String connName, connType;
          private String userName, password, role;
          private String driver, hostName, sid;
          private int jdbcPort;
          
          public int getMaxStep() {
          return MAX_STEP;
              }
          
          public int getCurrentStep() {
          return currentStep;
              }
          
          public void setCurrentStep(int currentStep) {
          this.currentStep = currentStep;
              }
          
              ...
          }
          
          currentStepmaxStep 之外,WizardBean 類還有幾個其他的屬性可用于保存用戶提供的向?qū)?shù):connNameconnType、userName、passwordroledriver、hostName、sidjdbcPort。該示例應(yīng)用程序與用戶數(shù)據(jù)無關(guān),但在實際情況中,向?qū)⒂盟鼇砼渲脭?shù)據(jù)庫連接。WizardBean 還實施了幾個在用戶單擊向?qū)У陌粹o時將執(zhí)行 JSF 操作。這些方法稍后將在本部分中進(jìn)行介紹。

          要創(chuàng)建您自己的 Bean,您可以在 File 菜單中選擇 New 來打開 New Gallery 窗口。然后,轉(zhuǎn)至 General 中的 Simple Files 類別,在右側(cè)面板中選擇 Java Class,并單擊 OK:

          圖 13

          提供類名和程序包名稱。然后單擊 OK,創(chuàng)建該類:

          圖 14

          在聲明一個字段之后(例如 private int currentStep),右鍵單擊其名稱并選擇 Generate Accessors。單擊 OK,生成 get 和 set 方法:

          圖 15

          創(chuàng)建可重用表單。將向?qū)У闹鞅韱尉帉憺榭芍赜脴?biāo)記文件。第一個表單 (form1.tag) 包含一個文本域和一個下拉列表:
          <!-- form1.tag -->
          
          <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>
          
          <af:inputText id="connName" required="true" columns="40"
          label="Connection Name:" value="#{wizard.connName}"
          readOnly="#{wizard.currentStep == wizard.maxStep}"/>
          
          <af:selectOneChoice id="connType" required="true"
          label="Connection Type:" value="#{wizard.connType}"
          readOnly="#{wizard.currentStep == wizard.maxStep}">
          <af:selectItem label="Oracle (JDBC)" value="Oracle_JDBC"/>
          </af:selectOneChoice>
          
          第二個表單 (form2.tag) 包含三個文本域:
          <!-- form2.tag -->
          
          <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>
          
          <af:inputText id="userName" required="true" columns="40"
          label="User Name:" value="#{wizard.userName}"
          readOnly="#{wizard.currentStep == wizard.maxStep}"/>
          
          <af:inputText id="password" required="true" columns="40"
          label="Password:" value="#{wizard.password}" secret="true"
          readOnly="#{wizard.currentStep == wizard.maxStep}"/>
          
          <af:inputText id="role" required="false" columns="40"
          label="Role:" value="#{wizard.role}"
          readOnly="#{wizard.currentStep == wizard.maxStep}"/>
          
          第三個表單 (form3.tag) 與其他兩個表單類似:
          <!-- form3.tag -->
          
          <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>
          
          <af:selectOneChoice id="driver" required="true"
          label="Driver:" value="#{wizard.driver}"
          readOnly="#{wizard.currentStep == wizard.maxStep}">
          <af:selectItem label="thin" value="thin"/>
          <af:selectItem label="oci8" value="oci8"/>
          </af:selectOneChoice>
          
          <af:inputText id="hostName" required="true" columns="40"
          label="Host Name:" value="#{wizard.hostName}"
          readOnly="#{wizard.currentStep == wizard.maxStep}"/>
          
          <af:inputText id="sid" required="true" columns="40"
          label="SID:" value="#{wizard.sid}"
          readOnly="#{wizard.currentStep == wizard.maxStep}"/>
          
          <af:inputText id="jdbcPort" required="true" columns="40"
          label="JDBC Port:" value="#{wizard.jdbcPort}"
          readOnly="#{wizard.currentStep == wizard.maxStep}"/>
          
          正如您可能已經(jīng)注意到的那樣,三個標(biāo)記文件中沒有一個包含了用于排列 Oracle ADF Faces 組件的標(biāo)記。不含任何布局標(biāo)記使得您可以獨立地使用表單標(biāo)記,或在確認(rèn)頁面中組合它們。Oracle ADF Faces 提供了一個名稱為 <af:panelForm> 的強(qiáng)大組件,它將自動執(zhí)行布局。除了主要的組件之外,表單通常包含有其他的標(biāo)記,例如 <h:messages globalOnly="true"/><af:objectLegend name="required"/>。所有這些標(biāo)記都可以集中在一個名為 formTemplate.tag 的標(biāo)記文件中:
          <!-- formTemplate.tag -->
          
          <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
          <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>
          
          <h:panelGrid columns="1" border="0" cellspacing="5">
          <h:messages globalOnly="true"/>
          <af:objectLegend name="required"/>
          <af:panelForm>
          <jsp:doBody/>
          </af:panelForm>
          </h:panelGrid>
          
          使用 pageTemplate.tag 和 formTemplate.tag 的 JSF 頁面將類似于:
          <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>
          <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
          
          <tags:pageTemplate step="..." title="...">
          	<af:form id="...">
          		...
          		<tags:formTemplate>
          			<tags:form123/>
          		</tags:formTemplate>
          		...
          	</af:form>
          </tags:pageTemplate>
          
          在前面的代碼段中,<tags:form123/> 代表向?qū)У娜齻€主表單中的任意一個(&lt;tags:form1/>、<tags:form2/><tags:form3/>)。除了這些表單的組件之外,<af:form> 可能包含其他的 JSF 和 Oracle ADF Faces 組件(例如按鈕)。下一段介紹了使用 <tags:pageTemplate><tags:formTemplate><tags:form1>、<tags:form2><tags:form3> 的應(yīng)用程序頁面。這些具體的例子充分說明了利用可重用標(biāo)記文件構(gòu)建 JSF 用戶界面的實際好處。

          向?qū)эL(fēng)格的界面。基于 Web 的向?qū)У捻撁鎸瑯?biāo)記為 Back、Next 和 Finish 的按鈕??梢栽谝粋€名為 stepButtons.tag 的標(biāo)記文件中定義這些按鈕:
          <!-- stepButtons.tag -->
          
          <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
          <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>
          
          <af:panelButtonBar>
          
          <af:singleStepButtonBar
          selectedStep="#{wizard.currentStep}"
          maxStep="#{wizard.maxStep}"
          previousAction="#{wizard.previousAction}"
          nextAction="#{wizard.nextAction}"/>
          
          <c:if test="${wizard.currentStep == wizard.maxStep}">
          <af:commandButton text="Finish"
          action="#{wizard.finishAction}"/>
          </c:if>
          
          </af:panelButtonBar>
          
          WizardBean 類包含當(dāng)用戶單擊按鈕時將執(zhí)行的操作方法:
          package webreuse;
          
          public class WizardBean implements java.io.Serializable {
              ...
          
          public String previousAction() {
          if (currentStep <= 1)
          return null;
          else {
          currentStep--;
          return "step" + currentStep;
                  }
              }
          
          public String nextAction() {
          if (currentStep >= getMaxStep())
          return null;
          else {
          currentStep++;
          return "step" + currentStep;
                  }
              }
          
          public String finishAction() {
          currentStep = 0;
          return "finished";
              }
          
              ...
          }
          
          操作方法返回的結(jié)果將在 faces-config.xml 文件的導(dǎo)航規(guī)則中使用:
          <faces-config>
              ...
          <navigation-rule>
          <from-view-id>*</from-view-id>
          <navigation-case>
          <from-outcome>step1</from-outcome>
          <to-view-id>/step1.jsp</to-view-id>
          </navigation-case>
          </navigation-rule>
              ...
          <navigation-rule>
          <from-view-id>*</from-view-id>
          <navigation-case>
          <from-outcome>step4</from-outcome>
          <to-view-id>/confirm.jsp</to-view-id>
          </navigation-case>
          </navigation-rule>
          
          <navigation-rule>
          <from-view-id>*</from-view-id>
          <navigation-case>
          <from-outcome>finished</from-outcome>
          <to-view-id>/index.jsp</to-view-id>
          </navigation-case>
          </navigation-rule>
              ...
          </faces-config>
          
          除了所有可見的組件之外,向?qū)ы撁孢€將包含一個與 WizardBean 的 currentStep 屬性綁定的隱藏字段。您已經(jīng)看到了 pageTemplate.tag 將在每一次執(zhí)行頁面時設(shè)置該屬性。然而,用戶可能單擊瀏覽器的后退按鈕。作為該操作的結(jié)果,在瀏覽器中看到的當(dāng)前步驟將與 currentStep 屬性的值不符,因為瀏覽器將從其緩存中檢索到頁面,而不是請求執(zhí)行 JSF 頁面。

          如果每一次用戶單擊按鈕時 currentStep 值都與表單數(shù)據(jù)一起提交,則不會導(dǎo)致任何問題。hiddenData.tag 文件將 currentStep 作為一個隱藏字段包含在內(nèi)。此外,如果 currentStep 等于 maxStep(這意味著標(biāo)記在確認(rèn)頁面中使用),那么該標(biāo)記文件將為所有 Bean 屬性生成隱藏字段:
          <!-- hiddenData.tag -->
          
          <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
          <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
          
          <h:inputHidden id="currentStep" value="#{wizard.currentStep}"/>
          
          <c:if test="${wizard.currentStep == wizard.maxStep}">
          <h:inputHidden id="h_connName" value="#{wizard.connName}"/>
          <h:inputHidden id="h_connType" value="#{wizard.connType}"/>
          <h:inputHidden id="h_userName" value="#{wizard.userName}"/>
          <h:inputHidden id="h_password" value="#{wizard.password}"/>
          <h:inputHidden id="h_role" value="#{wizard.role}"/>
          <h:inputHidden id="h_driver" value="#{wizard.driver}"/>
          <h:inputHidden id="h_hostName" value="#{wizard.hostName}"/>
          <h:inputHidden id="h_sid" value="#{wizard.sid}"/>
          <h:inputHidden id="h_jdbcPort" value="#{wizard.jdbcPort}"/>
          </c:if>
          
          所有的向?qū)Р糠侄伎梢栽?JSF 頁面中組裝,如 step1.jsp:
          <!-- step1.jsp -->
          
          <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>
          <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
          
          <tags:pageTemplate step="1" 
          title="Create Database Connection - Step 1">
          <af:form id="form1">
          <tags:formTemplate>
          <tags:form1/>
          </tags:formTemplate>
          <tags:stepButtons/>
          <tags:hiddenData/>
          </af:form>
          </tags:pageTemplate>
          
          step2.jsp 和 step3.jsp 頁面與 step1.jsp 非常類似。作為練習(xí),您可以嘗試為這些頁面構(gòu)建一個模板,從而將這些頁面都減少為四行代碼。confirm.jsp 頁面將一起顯示所有三個表單,但組件在只讀模式下工作,從而占用的屏幕空間更少并且無需用戶交互:
          <!-- confirm.jsp -->
          
          <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>
          <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
          
          <tags:pageTemplate step="4" title="Confirm Connection Parameters">
          <af:form id="form4">
          <tags:formTemplate>
          <tags:form1/>
          <tags:form2/>
          <tags:form3/>
          </tags:formTemplate>
          <tags:stepButtons/>
          <tags:hiddenData/>
          </af:form>
          </tags:pageTemplate>
          
          基于菜單的界面。假定用戶逐步瀏覽向?qū)У捻撁?,提供所有需要的信息。如果用戶需要在以后修改某些地方,那么他?yīng)當(dāng)不需要再次瀏覽所有的向?qū)ы撁?。相反,用戶界面將讓用戶直接轉(zhuǎn)至必須修改信息的表單。menuTabs.tag 文件使用相同名稱的 Oracle ADF Faces 組件來構(gòu)建菜單:
          <!-- menuTabs.tag -->
          
          <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>
          
          <af:menuTabs>
          <af:commandMenuItem text="Name and Type" action="tab1"
          selected="#{wizard.currentStep == 1}"/>
          <af:commandMenuItem text="Authentication" action="tab2"
          selected="#{wizard.currentStep == 2}"/>
          <af:commandMenuItem text="Connection" action="tab3"
          selected="#{wizard.currentStep == 3}"/>
          </af:menuTabs>
          
          菜單的導(dǎo)航規(guī)則在 faces-config.xml 中定義:
          <faces-config>
              ...
          <navigation-rule>
          <from-view-id>*</from-view-id>
          <navigation-case>
          <from-outcome>tab1</from-outcome>
          <to-view-id>/tab1.jsp</to-view-id>
          </navigation-case>
          </navigation-rule>
              ...
          <navigation-rule>
          <from-view-id>*</from-view-id>
          <navigation-case>
          <from-outcome>tab3</from-outcome>
          <to-view-id>/tab3.jsp</to-view-id>
          </navigation-case>
          </navigation-rule>
              ...
          </faces-config>
          
          當(dāng)用戶單擊菜單的標(biāo)簽時,表單數(shù)據(jù)將被提交給 Web 服務(wù)器,在該服務(wù)器上 JSF 框架將更新 Backing Bean。如果用戶想更新表單而不改變當(dāng)前的標(biāo)簽,那么需要使用提交按鈕。submitButton.tag 文件提供了提交按鈕:
          <!-- submitButton.tag -->
          
          <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>
          
          <af:commandButton id="command" text="Submit" action="submitAction"/>
          
          tab1.jsp、tab2.jsp 和 tab3.jsp 文件將把菜單附加到向?qū)У谋韱紊?。下面?tab1.jsp:
          <!-- tab1.jsp -->
          
          <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>
          <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
          
          <tags:pageTemplate step="1" title="Edit Database Connection">
          <af:form id="form1">
          <tags:menuTabs/>
          <tags:formTemplate>
          <tags:form1/>
          </tags:formTemplate>
          <tags:submitButton/>
          <tags:hiddenData/>
          </af:form>
          </tags:pageTemplate>
          
          使顯示邏輯可重用

          如果您必須使用 JSF 和 Oracle ADF Faces 從頭開始構(gòu)建新的頁面,那么一切都沒什么問題。但是,對于包含了以 Java 代碼形式存在的顯示邏輯的舊 JSP 頁面,該如何處理呢?維護(hù)這些頁面困難重重,并且,若不將顯示代碼從 JSP 頁面中分離出來,則無法對其進(jìn)行重用。此外,Java 代碼和 HTML 標(biāo)記不應(yīng)混合在同一個頁面中,因為這將使 Java 開發(fā)人員和 Web 設(shè)計人員無法輕松地展開并行工作。

          JSF 和 Oracle ADF Faces 組件解決了多數(shù)情況下的此種問題,因為 Web 頁面將使用這兩個框架提供的標(biāo)記來構(gòu)建,同時將 Java 代碼放到了 Backing Bean 中。JSTL 和其他的標(biāo)記庫也非常有用,但有時您必須只能使用 Java 代碼來動態(tài)生成內(nèi)容。

          一種好的解決方案是使用 JSP 2.0 提供的 Simple Tags API 來構(gòu)建標(biāo)記庫。該 API 使您能夠開發(fā)標(biāo)記處理器類,但您必須維護(hù)一個單獨的 XML 文件(稱為標(biāo)記庫描述符 (TLD)),該文件定義標(biāo)記名稱、它們的屬性等。如果這聽起來太復(fù)雜,那么您可以簡單地將 Java 代碼移到使用 JSP 語法的標(biāo)記文件中,讓應(yīng)用服務(wù)器生成標(biāo)記處理器類和 TLD 文件。讓我們看一下名為 oldCode.jsp 的 JSP 頁面,它混合了 Java 和 HTML。該頁面將讀取一個文本文件(其路徑將作為一個請求參數(shù)提供)并顯示文件的內(nèi)容(包括行號)。當(dāng)您構(gòu)建演示應(yīng)用程序并想顯示代碼時,這將非常有用。

          重要注意事項!請勿在生產(chǎn)環(huán)境中使用本部分的示例(oldCode.jsp 和 newCode.jsp),因為它們可能會泄漏應(yīng)用程序的源代碼。

          oldCode.jsp 頁面使用 java.io API 來讀取文本文件。它將在 JSP 頁面范圍中為每一行文本創(chuàng)建兩個名為 lineTextlineNo 的變量。行號將用 JSTL 的 <fmt:formatNumber> 標(biāo)記來進(jìn)行格式化,文本將通過 <c:out> 標(biāo)記進(jìn)行顯示:
          <!-- oldCode.jsp -->
          
          <%@ page import="java.io.*" %>
          <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
          <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
          
          <HTML>
          <HEAD>
          <TITLE>${param.path}</TITLE>
          </HEAD>
          <BODY>
          
          <c:if test="${empty param.path}">
          <P>The <CODE>path</CODE> parameter wasn't specified.
          </c:if>
          
          <c:if test="${!empty param.path}">
          <P><B><CODE>${param.path}</CODE></B>
          <%
          String path = application.getRealPath(
          request.getParameter("path"));
          BufferedReader in = new BufferedReader(new FileReader(path));
          try {
          int lineNo = 0;
          String lineText;
          while ((lineText = in.readLine()) != null) {
          lineNo++;
          pageContext.setAttribute("lineText", lineText);
          pageContext.setAttribute("lineNo",
          new Integer(lineNo));
          %>
          <fmt:formatNumber var="fmtLineNo"
          value="${lineNo}" minIntegerDigits="3"/>
          <PRE>${fmtLineNo}  <c:out value="${lineText}"/></PRE>
          <%
                  }
          } finally {
          in.close();
              }
          %>
          </c:if>
          
          </BODY>
          </HTML>
          
          來自前一個頁面示例的全部 Java 代碼都可以移到一個名為 readTextFile.tag 的標(biāo)記文件中。只需進(jìn)行少許修改:您必須使用 <%@tag%> 指令和 jspContext 隱式對象,而不是 <%@page%> 和 pageContext。您還必須使用 JSP 指令來聲明屬性和變量:
          <!-- readTextFile.tag -->
          
          <%@ tag import="java.io.*" %>
          <%@ attribute name="path" required="true" %>
          <%@ variable name-given="lineText" scope="NESTED" %>
          <%@ variable name-given="lineNo" scope="NESTED" %>
          <%
          String path = application.getRealPath(
          (String) jspContext.getAttribute("path"));
          BufferedReader in = new BufferedReader(new FileReader(path));
          try {
          int lineNo = 0;
          String lineText;
          while ((lineText = in.readLine()) != null) {
          lineNo++;
          jspContext.setAttribute("lineText", lineText);
          jspContext.setAttribute("lineNo",
          new Integer(lineNo));
          %>
          <jsp:doBody/>
          <%
                  }
          } finally {
          in.close();
              }
          %>
          
          您可以在任何需要逐行處理文本文件的 JSP 頁面中使用 readTextFile.tag 文件。每一個頁面都可以對文本行執(zhí)行任何需要的操作,因為該標(biāo)記文件使用了 <jsp:doBody/>,從而允許 JSP 頁面將處理當(dāng)前行的代碼放到 <tags:readTextFile></tags:readTextFile> 之間。newCode.jsp 頁面的功能與舊樣式的示例相同,但它沒有混合 Java 和 HTML:
          <!-- newCode.jsp -->
          
          <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
          <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
          <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
          
          <HTML>
          <HEAD>
          <TITLE>${param.path}</TITLE>
          </HEAD>
          <BODY>
          
          <c:if test="${empty param.path}">
          <P>The <CODE>path</CODE> parameter wasn't specified.
          </c:if>
          
          <c:if test="${!empty param.path}">
          <P><B><CODE>${param.path}</CODE></B>
          <tags:readTextFile path="${param.path}">
          <fmt:formatNumber var="fmtLineNo"
          value="${lineNo}" minIntegerDigits="3"/>
          <PRE>${fmtLineNo}  <c:out value="${lineText}"/></PRE>
          </tags:readTextFile>
          </c:if>
          
          </BODY>
          </HTML>
          
          正如您所見,修改舊的 JSP 頁面以便可以重用代碼并不是很難。維護(hù)也變得更加容易。

          隨意重用

          重復(fù)的代碼或內(nèi)容是最令人頭疼的事情。有時它可能是一種容易的解決方案,但修改應(yīng)用程序變得更加困難,這意味著從長遠(yuǎn)來看,您將不能適應(yīng)新的用戶需求或快速地修復(fù)問題。在理想情況下,應(yīng)用程序不應(yīng)包含相同代碼的多個版本,Web 內(nèi)容不應(yīng)被復(fù)制和粘貼。
          posted on 2006-05-10 14:53 KingWell 閱讀(383) 評論(0)  編輯  收藏 所屬分類: Java Server Faces

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          歡迎訪問我的網(wǎng)站
          JSF中國

          <2025年5月>
          27282930123
          45678910
          11121314151617
          18192021222324
          25262728293031
          1234567

          常用鏈接

          留言簿(1)

          隨筆檔案

          文章分類

          文章檔案

          收藏夾

          我的資源

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 象州县| 泾川县| 十堰市| 佛学| 沁水县| 公主岭市| 炉霍县| 麦盖提县| 花莲市| 涞源县| 溆浦县| 阜南县| 进贤县| 满城县| 江安县| 洛阳市| 太谷县| 齐河县| 探索| 雅安市| 两当县| 醴陵市| 白山市| 清涧县| 山东| 武威市| 淮安市| 华阴市| 辽阳县| 闵行区| 武冈市| 石狮市| 福建省| 上饶县| 辉县市| 海兴县| 通道| 织金县| 普陀区| 德钦县| 那坡县|