世事如棋
          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è)計(jì)良好的框架和可自定義的組件,而不是從頭重來(lái)。應(yīng)用程序特有的代碼也可以在模塊甚至相關(guān)項(xiàng)目間重用。后一種可重用性可使您快速修改,整體利用新特性,并減少測(cè)試和調(diào)試的時(shí)間。

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

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

          Java Web 可重用特性

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

          對(duì)自定義標(biāo)記的支持從 JSP 1.1 就有了,它為構(gòu)建標(biāo)記庫(kù)提供了一個(gè) API。JSP 1.2 對(duì)該 API 進(jìn)行了增強(qiáng),但很多人認(rèn)為它太復(fù)雜了。因此,JSP 2.0 定義了一個(gè)具有相同功能的全新 API。這個(gè)為標(biāo)記庫(kù)提供的新 API 稱為簡(jiǎn)單標(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 一起使用。簡(jiǎn)單標(biāo)記 API 是另一種 JSP 2.0 特性 — 標(biāo)記文件 — 的基礎(chǔ),該特性使您能夠使用 JSP 語(yǔ)法構(gòu)建標(biāo)記庫(kù)。除了簡(jiǎn)單標(biāo)記和標(biāo)記文件之外,JSP 2.0 規(guī)范還定義了 EL 函數(shù),后者使您能夠使用 EL 語(yǔ)法從 JSP 頁(yè)面中調(diào)用靜態(tài) Java 方法。

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

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

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

          圖 1

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

          圖 2

          為模板提供一個(gè)文件名:

          圖 3

          選擇組件綁定樣式:

          圖 4

          指定是否要使用錯(cuò)誤頁(yè)面:

          圖 5

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

          圖 6

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

          圖 7

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

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

          圖 8

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

          圖 9

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

          圖 10

          單擊 Next 和 Finish。JDeveloper 將在 WEB-INF 目錄的 tags 子目錄下創(chuàng)建 pageTemplate.tag 文件。用 <%@attribute%> 指令定義這兩個(gè)標(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。無(wú)論您是否想在普通頁(yè)面的標(biāo)記文件內(nèi)使用這些框架,您都必須用 <%@taglib%> 指令來(lái)對(duì)其進(jìn)行聲明。在 Component Palette 中選擇 JSP,然后單擊 Taglib。您需要在一個(gè)對(duì)話框中輸入要使用的標(biāo)記庫(kù)的 URI 和前綴:

          圖 11

          您還可以使用 Component Palette 來(lái)將任何標(biāo)記拖放到 JSP 頁(yè)面上,如果它的 <%@taglib%> 指令不在頁(yè)面中,那么 JDeveloper 將自動(dòng)添加它。pageTemplate.tag 示例使用了四個(gè)標(biāo)記庫(kù):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" %>
          
          在同時(shí)使用 JSF 和 JSTL 時(shí),或者在標(biāo)記文件中使用 JSF 時(shí)有一件非常重要的事情您必須始終牢記:JSF 框架不支持頁(yè)面范圍,后者是 JSTL 標(biāo)記的默認(rèn) JSP 范圍。因此,您應(yīng)當(dāng)顯式指定與 JSTL、JSF 和 Oracle ADF Faces 的標(biāo)記結(jié)合使用的 JSP 變量的請(qǐng)求范圍。此外,標(biāo)記文件的所有屬性都可以通過(guò)保存在頁(yè)面范圍內(nèi)的 JSP 變量來(lái)訪問(wèn)。您必須復(fù)制請(qǐng)求范圍內(nèi)的屬性,以便它們可以和 JSF 和 Oracle ADF Faces 標(biāo)記一起使用:
          <c:set var="pageTitle" scope="request" value="${pageScope.title}"/>
          
          您還可以將屬性的值存儲(chǔ)在由 JSF 管理的 Bean 內(nèi)。假定您有一個(gè)名稱為 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 有一個(gè) currentStep 屬性,您可以在該屬性中存儲(chǔ) step 屬性的值。該操作可以利用 JSTL 的 <c:set> 標(biāo)記來(lái)完成,但您必須確保該 Bean 存在于會(huì)話范圍中(如 faces-config.xml 文件中所聲明的那樣)。只有在 JSF EL 表達(dá)式中首次引用受管 Bean 實(shí)例時(shí),JSF 才會(huì)創(chuàng)建該實(shí)例。如果在訪問(wèn)受管 Bean 的 JSF 或 Oracle ADF Faces 標(biāo)記之前執(zhí)行 JSTL 標(biāo)記,那么,您應(yīng)當(dāng)使用 <jsp:useBean>以確保該 Bean 實(shí)例存在于會(huì)話范圍中。如此,您就可以安全地使用 <c:set> 標(biāo)記了:
          <jsp:useBean class="webreuse.WizardBean" 
          id="wizard" scope="session"/>
          <c:set target="${wizard}" property="currentStep" 
          value="${pageScope.step}"/>
          
          以上代碼可以手動(dòng)輸入,或者可以使用 JDeveloper 的 Component Palette。選擇 JSP,然后單擊 UseBean,添加 <jsp:useBean> 標(biāo)記。JDeveloper 將打開一個(gè)對(duì)話框,您必須在其中提供該標(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> 組件顯示頁(yè)面標(biāo)題。然后,<jsp:doBody> 將調(diào)用您放在使用模板標(biāo)記的 JSP 頁(yè)面中的 <tags:pageTemplate></tags:pageTemplate> 之間的 JSP 內(nèi)容。這種 JSP 頁(yè)面將類似于:
          <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
          
          <tags:pageTemplate step="..." title="...">
          ... JSP content executed by doBody ...
          </tags:pageTemplate>
          
          當(dāng)執(zhí)行該頁(yè)面時(shí),標(biāo)記文件將生成 <html><head><body> 標(biāo)記以及頁(yè)面標(biāo)題和其他標(biāo)題標(biāo)記。然后,模板標(biāo)記文件將調(diào)用包在 <tags:pageTemplate></tags:pageTemplate> 之間的 JSP 內(nèi)容。在此之后,標(biāo)記文件可能會(huì)生成一個(gè)頁(yè)腳,并用 </body></html> 來(lái)完成頁(yè)面。下一部分將在基于 Web 的向?qū)У捻?yè)面中使用模板標(biāo)記。

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

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

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

          開發(fā) Backing Bean。您在前一部分中已經(jīng)發(fā)現(xiàn),示例應(yīng)用程序使用了一個(gè)名稱為 WizardBean 的 Backing Bean。pageTemplate.tag 文件設(shè)置了 currentStep 屬性,該屬性保存用戶在瀏覽器中看到的當(dāng)前表單的步驟 ID。示例應(yīng)用程序在確認(rèn)頁(yè)面(第四步)之前使用了三個(gè)表單,確認(rèn)頁(yè)面的 ID 由 MAX_STEP 常量來(lái)定義。將該常量公開為名為 maxStep 的 bean 屬性,以便可以在 Web 頁(yè)面中使用 JSF EL 來(lái)訪問(wèn)它:
          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 類還有幾個(gè)其他的屬性可用于保存用戶提供的向?qū)?shù):connNameconnTypeuserNamepasswordroledriverhostNamesidjdbcPort。該示例應(yīng)用程序與用戶數(shù)據(jù)無(wú)關(guān),但在實(shí)際情況中,向?qū)⒂盟鼇?lái)配置數(shù)據(jù)庫(kù)連接。WizardBean 還實(shí)施了幾個(gè)在用戶單擊向?qū)У陌粹o時(shí)將執(zhí)行 JSF 操作。這些方法稍后將在本部分中進(jìn)行介紹。

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

          圖 13

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

          圖 14

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

          圖 15

          創(chuàng)建可重用表單。將向?qū)У闹鞅韱尉帉憺榭芍赜脴?biāo)記文件。第一個(gè)表單 (form1.tag) 包含一個(gè)文本域和一個(gè)下拉列表:
          <!-- 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>
          
          第二個(gè)表單 (form2.tag) 包含三個(gè)文本域:
          <!-- 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}"/>
          
          第三個(gè)表單 (form3.tag) 與其他兩個(gè)表單類似:
          <!-- 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)注意到的那樣,三個(gè)標(biāo)記文件中沒(méi)有一個(gè)包含了用于排列 Oracle ADF Faces 組件的標(biāo)記。不含任何布局標(biāo)記使得您可以獨(dú)立地使用表單標(biāo)記,或在確認(rèn)頁(yè)面中組合它們。Oracle ADF Faces 提供了一個(gè)名稱為 <af:panelForm> 的強(qiáng)大組件,它將自動(dòng)執(zhí)行布局。除了主要的組件之外,表單通常包含有其他的標(biāo)記,例如 <h:messages globalOnly="true"/><af:objectLegend name="required"/>。所有這些標(biāo)記都可以集中在一個(gè)名為 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 頁(yè)面將類似于:
          <%@ 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ū)У娜齻€(gè)主表單中的任意一個(gè)(&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)用程序頁(yè)面。這些具體的例子充分說(shuō)明了利用可重用標(biāo)記文件構(gòu)建 JSF 用戶界面的實(shí)際好處。

          向?qū)эL(fēng)格的界面。基于 Web 的向?qū)У捻?yè)面將包含標(biāo)記為 Back、Next 和 Finish 的按鈕。可以在一個(gè)名為 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)用戶單擊按鈕時(shí)將執(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ū)ы?yè)面還將包含一個(gè)與 WizardBean 的 currentStep 屬性綁定的隱藏字段。您已經(jīng)看到了 pageTemplate.tag 將在每一次執(zhí)行頁(yè)面時(shí)設(shè)置該屬性。然而,用戶可能單擊瀏覽器的后退按鈕。作為該操作的結(jié)果,在瀏覽器中看到的當(dāng)前步驟將與 currentStep 屬性的值不符,因?yàn)闉g覽器將從其緩存中檢索到頁(yè)面,而不是請(qǐng)求執(zhí)行 JSF 頁(yè)面。

          如果每一次用戶單擊按鈕時(shí) currentStep 值都與表單數(shù)據(jù)一起提交,則不會(huì)導(dǎo)致任何問(wèn)題。hiddenData.tag 文件將 currentStep 作為一個(gè)隱藏字段包含在內(nèi)。此外,如果 currentStep 等于 maxStep(這意味著標(biāo)記在確認(rèn)頁(yè)面中使用),那么該標(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 頁(yè)面中組裝,如 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 頁(yè)面與 step1.jsp 非常類似。作為練習(xí),您可以嘗試為這些頁(yè)面構(gòu)建一個(gè)模板,從而將這些頁(yè)面都減少為四行代碼。confirm.jsp 頁(yè)面將一起顯示所有三個(gè)表單,但組件在只讀模式下工作,從而占用的屏幕空間更少并且無(wú)需用戶交互:
          <!-- 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è)面,提供所有需要的信息。如果用戶需要在以后修改某些地方,那么他應(yīng)當(dāng)不需要再次瀏覽所有的向?qū)ы?yè)面。相反,用戶界面將讓用戶直接轉(zhuǎn)至必須修改信息的表單。menuTabs.tag 文件使用相同名稱的 Oracle ADF Faces 組件來(lái)構(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í),表單數(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ū)У谋韱紊稀O旅媸?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)建新的頁(yè)面,那么一切都沒(méi)什么問(wèn)題。但是,對(duì)于包含了以 Java 代碼形式存在的顯示邏輯的舊 JSP 頁(yè)面,該如何處理呢?維護(hù)這些頁(yè)面困難重重,并且,若不將顯示代碼從 JSP 頁(yè)面中分離出來(lái),則無(wú)法對(duì)其進(jìn)行重用。此外,Java 代碼和 HTML 標(biāo)記不應(yīng)混合在同一個(gè)頁(yè)面中,因?yàn)檫@將使 Java 開發(fā)人員和 Web 設(shè)計(jì)人員無(wú)法輕松地展開并行工作。

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

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

          重要注意事項(xiàng)!請(qǐng)勿在生產(chǎn)環(huán)境中使用本部分的示例(oldCode.jsp 和 newCode.jsp),因?yàn)樗鼈兛赡軙?huì)泄漏應(yīng)用程序的源代碼。

          oldCode.jsp 頁(yè)面使用 java.io API 來(lái)讀取文本文件。它將在 JSP 頁(yè)面范圍中為每一行文本創(chuàng)建兩個(gè)名為 lineTextlineNo 的變量。行號(hào)將用 JSTL 的 <fmt:formatNumber> 標(biāo)記來(lái)進(jìn)行格式化,文本將通過(guò) <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>
          
          來(lái)自前一個(gè)頁(yè)面示例的全部 Java 代碼都可以移到一個(gè)名為 readTextFile.tag 的標(biāo)記文件中。只需進(jìn)行少許修改:您必須使用 <%@tag%> 指令和 jspContext 隱式對(duì)象,而不是 <%@page%> 和 pageContext。您還必須使用 JSP 指令來(lái)聲明屬性和變量:
          <!-- 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 頁(yè)面中使用 readTextFile.tag 文件。每一個(gè)頁(yè)面都可以對(duì)文本行執(zhí)行任何需要的操作,因?yàn)樵摌?biāo)記文件使用了 <jsp:doBody/>,從而允許 JSP 頁(yè)面將處理當(dāng)前行的代碼放到 <tags:readTextFile></tags:readTextFile> 之間。newCode.jsp 頁(yè)面的功能與舊樣式的示例相同,但它沒(méi)有混合 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 頁(yè)面以便可以重用代碼并不是很難。維護(hù)也變得更加容易。

          隨意重用

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

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


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

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

          常用鏈接

          留言簿(1)

          隨筆檔案

          文章分類

          文章檔案

          收藏夾

          我的資源

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 长阳| 日喀则市| 西畴县| 莒南县| 河南省| 衡阳市| 江都市| 紫金县| 蒲城县| 枣阳市| 塔河县| 丰原市| 杨浦区| 策勒县| 山东省| 盘山县| 泸定县| 高淳县| 渝中区| 农安县| 荔浦县| 云龙县| 沙河市| 霞浦县| 丁青县| 滨海县| 连州市| 古蔺县| 博客| 大城县| 乐清市| 登封市| 南乐县| 思南县| 宁乡县| 淳安县| 桦甸市| 迭部县| 义马市| 朔州市| 高清|