<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    閔毓
    http://www.eshoo.com.cn 歡迎來到異客中國
    posts - 49,comments - 97,trackbacks - 0
    將三種技術(shù)的功能、靈活性和可管理性集成到一起
    您是否想將 JavaServer Faces (JSF)的強(qiáng)大前端功能、Tiles 的內(nèi)容格式編排優(yōu)勢(shì)和 Struts controller 層的靈活性都加入到您的J2EE Web 應(yīng)用程序中?企業(yè)級(jí) Java 專家 Srikanth Shenoy 和 Nithin Mallya 為您展示了如何將這三者的功能集成到一起。本文演示了如何在 Struts-Faces集成庫中定制類以使得它們可以與 Tiles 和 JSF 一同使用,并用一個(gè)實(shí)際的例子解釋了這個(gè)過程背后的基本原理以及如何使用新的一組類的細(xì)節(jié)。

    將 Struts、Tiles 和 JavaServer Faces (JSF) 一起使用,開發(fā)人員可以實(shí)現(xiàn)易于管理和重用的、健壯的、界面清晰的 Web 應(yīng)用程序。

    Struts 框架推出已經(jīng)有一段時(shí)間了,它已經(jīng)成為在開發(fā) J2EE Web 應(yīng)用程序時(shí)開發(fā)人員所采用的事實(shí)上的標(biāo)準(zhǔn)。Tiles 框架是在 Struts 之后不久出現(xiàn)的,它通過為開發(fā)人員提供用組件組裝展示頁面的能力開拓了自己的生存環(huán)境。JSF 是 Web 應(yīng)用程序框架中最新的成員,它提供了驗(yàn)證用戶輸入和處理用戶事件的機(jī)制,最重要的是,這是一種以協(xié)議無關(guān)的方式呈現(xiàn)用戶界面組件的方法(有關(guān)這些 技術(shù)的概況,參見本文相關(guān)頁面“ The major players”)。

    盡管 Struts 和 JSF 中有一些功能是重疊的,但是它們?cè)谄渌矫嫫鸬搅嘶檠a(bǔ)充的作用。這三種技術(shù)的結(jié)合可以為開發(fā) Web 應(yīng)用程序、組織其展示和以協(xié)議無關(guān)的方式呈現(xiàn)定制的用戶界面(UI)組件提供一種高效的途徑。

    為了運(yùn)行本文中的示例代碼,需要 Struts 1.1、Tiles、JavaServer Faces Reference Implementation (JSF-RI) Early Access Release 4.0 以及 Struts-Faces 0.4。Jakarta 項(xiàng)目提供的 Struts 1.1 發(fā)行版本將 Struts 和 Tiles 捆綁發(fā)布。還可以從 Jakarta 項(xiàng)目上下載 Struts-Faces 集成庫。JSF-RI 是 Sun 的 Web 開發(fā)工具包(Web Services Developer Pack)的一部分(在 參考資料中有這些下載和示例代碼的鏈接)。

    現(xiàn)在回到集成三種技術(shù)的細(xì)節(jié)上。首先有個(gè)壞消息:在本文發(fā)表的時(shí)候,這三種技術(shù)是不能直接互操作的。好消息是:在本文中,我們展示了集成 Struts、Tiles 和 JSF 的方法。我們假設(shè)您已經(jīng)了解 Struts 和 Tiles。對(duì) JSF 有一些了解會(huì)有幫助(參閱 參考資料中提供的 developerWorks 上的 JSF 教程的鏈接),但是不了解也不妨礙對(duì)本文的理解。

    JSF 簡介
    JSF 應(yīng)用程序是使用 JSF 框架的普通 J2EE Web 應(yīng)用程序,JSF 框架提供了豐富的 GUI 組件模型,這些模型體現(xiàn)了真正的 GUI 框架內(nèi)涵。您可能聽人們說過,盡管某種技術(shù)不錯(cuò),但是它的外觀仍然需要改進(jìn)。是的,用 HTML 組件構(gòu)建平淡無奇的頁面的日子已經(jīng)過去了,如果使用 JSF 的話,具有更高級(jí) GUI 外觀的日子就在眼前。您會(huì)問,怎么做呢?樹形組件、菜單組件和圖形是已經(jīng)存在的 UI 組件,這些 JSF 一定要提供。更進(jìn)一步,JSF 通過提供容易使用的 API 鼓勵(lì)創(chuàng)建自定義組件。

    注: 這里所提到的 UI 組件是 Sun 提供的示例的一部分。像所有規(guī)范一樣,實(shí)際的實(shí)現(xiàn)由不同的提供商完成。

    在傳統(tǒng)的使用模型-視圖-控制器(MVC)的 Web 應(yīng)用程序中,GUI 組件是由處理展示和業(yè)務(wù)邏輯的自定義標(biāo)記所表示的。這樣就出現(xiàn)了必須“編寫與客戶機(jī)設(shè)備打交道的代碼”的問題,這會(huì)產(chǎn)生重復(fù)的代碼。使用 JSF 就不會(huì)有這個(gè)問題。

    JSF 結(jié)構(gòu)將 展示邏輯 (“什么”)與 UI 組件的 業(yè)務(wù)邏輯 (“為什么”和“如何”)分離。通過在 JSP 頁面中使用 JSF 標(biāo)記,就可以將 renderer 與 UI 組件關(guān)聯(lián)在一起。一個(gè) UI 組件可以用不同的 renderer 從而以不同的方式呈現(xiàn)。特定于 UI 組件的代碼在服務(wù)器上運(yùn)行,并且響應(yīng)用戶操作所產(chǎn)生的事件。

    JSF-RI 提供了一個(gè) render kit,它帶有一個(gè)自定義標(biāo)記庫,用以從 UI 組件呈現(xiàn) HTML。它還提供了根據(jù)需要定制這些組件外觀的能力。如果需要特殊的組件,那么可以為特定的客戶機(jī)設(shè)備構(gòu)造定制的標(biāo)記并讓它與一個(gè)子 UI 組件和定制的 renderer 相關(guān)聯(lián)。對(duì)于不同的設(shè)備,您所需要做的就是指定不同的 renderer。

    JSF 和 UI 組件
    您可能已經(jīng)用 Java AWT 或者 Swing API 創(chuàng)建過 Java GUI 應(yīng)用程序,所以您應(yīng)該熟悉 JSF 的 UIComponent (它與 AWT 或者 Swing 組件很相像)。它儲(chǔ)存其子組件的樹(如果有的話)并為客戶端發(fā)生的動(dòng)作生成標(biāo)準(zhǔn)事件,例如單擊一個(gè)按鈕以提交表單。這些事件緩存在 FacesContext 中。您可以用自定義標(biāo)記關(guān)聯(lián)每一個(gè)這種事件的處理程序。例如,用一個(gè)自定義的 ActionListener 處理用戶單擊或者表單提交。

    JSF UIComponentRenderer 和標(biāo)記總是共同工作的。所有 JSP 自定義標(biāo)記都是通過繼承 UIComponentTag 創(chuàng)建的。 doStartdoEnd 方法總是在 UIComponentTag 類中實(shí)現(xiàn)。您只需在這些標(biāo)記類中提供其他的功能。

    圖 1展示了自定義標(biāo)記、UI 組件和 renderer 之間的關(guān)系。客戶機(jī)瀏覽器訪問用 JSF 標(biāo)記( jsf:myTag )表示 UI 組件( MyComponent )的 JSP 頁面。這個(gè) UI 組件運(yùn)行在服務(wù)器上,并用適當(dāng)?shù)?renderer ( MyRenderer )以 HTML 的形式呈現(xiàn)給客戶。這個(gè) JSP 頁面表現(xiàn)了在 JSF-RI 中使用帶自定義標(biāo)記的用戶界面組件而不是在 HTML 中對(duì)它們進(jìn)行編碼。

    例如,圖 1 展示了 h:panel:group 標(biāo)記的使用。這個(gè)標(biāo)記用于將一個(gè)父組件下面的各個(gè)組件組織到一起。如果與像 panel_gridpanel_data 這樣的其他面板標(biāo)記共同使用,那么它會(huì)在運(yùn)行時(shí)生成 HTML 表中的列的標(biāo)記。JSF-RI-提供的 html_basic 標(biāo)記庫用于表示像文本字段、按鈕這樣的 HTML 組件。

    圖1. 呈現(xiàn)一個(gè) JSF 頁面
    Rendering a JSF page

    JSF 生命周期
    JSF 生命周期包括六個(gè)階段:一個(gè)傳入的請(qǐng)求可能會(huì)經(jīng)歷全部階段,也可能不經(jīng)歷任何階段,這取決于請(qǐng)求的類型、在生命周期中發(fā)生的驗(yàn)證和轉(zhuǎn)換錯(cuò)誤以及響應(yīng)的類型。JSF 框架處理由 JSP 頁生成的 Faces 請(qǐng)求,并返回 faces或者 non-faces 響應(yīng)

    在提交一個(gè) JSF 表單,或者當(dāng)用戶單擊指向在 URL 中具有 /faces 前綴的 URL 的鏈接時(shí),就會(huì)出現(xiàn) faces 響應(yīng)。所有 faces 請(qǐng)求都由一個(gè) FacesServlet 處理 -- 這是 JSF 中的控制器。

    發(fā)送給一個(gè) servlet 或者一個(gè)沒有 JSF 組件的 JSP 頁面的請(qǐng)求稱為 non-faces 請(qǐng)求。如果結(jié)果頁中有 JSF 標(biāo)記,那么它就稱為 faces 響應(yīng),如果沒有 JSF 標(biāo)記,就是 non-faces 響應(yīng)

    JSF 生命周期有六個(gè)階段:

    • 重建請(qǐng)求樹
    • 應(yīng)用請(qǐng)求值
    • 進(jìn)行驗(yàn)證
    • 更新模型值
    • 調(diào)用應(yīng)用程序
    • 呈現(xiàn)響應(yīng)

    根據(jù) JSF 規(guī)范,每一階段表示請(qǐng)求處理生命周期的一個(gè)邏輯概念。不過在 JSF-RI 中,這些階段是由具有對(duì)應(yīng)名字的實(shí)際類表示的。下面一節(jié)描述了每一階段是如何對(duì)請(qǐng)求進(jìn)行處理并生成響應(yīng)的。您將首先看到的是處理一個(gè) faces 請(qǐng)求所涉及的階段,然后是處理 faces 響應(yīng)所涉及的階段。

    處理 faces 請(qǐng)求
    為了理解 JSF 請(qǐng)求處理,請(qǐng)看 FlightSearch.jsp,這是 清單 1中的一個(gè)簡單的 JSF 表單。一個(gè) JSF 頁面基本上就是這個(gè)樣子的。這個(gè) JSF 表單有輸入文本字段 fromto citiesdeparturereturn dates,還有提交和重設(shè)表單的按鈕(我們會(huì)在稍后分析清單1中每一個(gè)標(biāo)記的意義)。現(xiàn)在,假設(shè)提交這個(gè)表單產(chǎn)生了一個(gè) faces 請(qǐng)求。

    這個(gè)請(qǐng)求被 FacesServlet 所接收、并在向客戶發(fā)回響應(yīng)之前通過不同的階段。 圖 2展示了如何對(duì) JSF 請(qǐng)求進(jìn)行處理。讓我們看一看這是如何進(jìn)行的。

    1. 接收請(qǐng)求
    FacesServlet 接收請(qǐng)求并從 FacesContextFactory 得到 FacesContext 的一個(gè)實(shí)例。

    2. 委托生命周期處理
    FacesServlet 通過對(duì)在 faces 上下文中傳遞的 Lifecycle 實(shí)現(xiàn)調(diào)用 execute 方法將生命周期處理委托給 Lifecycle 接口。

    3. Lifecycle 執(zhí)行每一階段
    Lifecycle 實(shí)現(xiàn)執(zhí)行從重建組件樹階段開始的每一階段。

    4. 創(chuàng)建的組件樹
    在重建組件樹階段,用 travelForm 中的組件創(chuàng)建一個(gè)組件樹。這個(gè)樹以 UIForm 作為根,用不同的文本字段和按鈕作為其子組件。

    fromCity 字段有一個(gè)驗(yàn)證規(guī)則,它規(guī)定其不能為空,如 validate_required 標(biāo)記所示。這個(gè)標(biāo)記將 fromCity 文本字段與一個(gè) JSF Validator 鏈接起來。

    JSF 有幾個(gè)內(nèi)建的驗(yàn)證器。相應(yīng)的 Validator 是在這個(gè)階段初始化的。這個(gè)組件樹緩存在 FacesContext 中、并且這個(gè)上下文會(huì)在后面用于訪問樹及調(diào)用任何一個(gè)事件處理程序。同時(shí) UIForm 狀態(tài)會(huì)自動(dòng)保存。所以,當(dāng)刷新這一頁時(shí),就會(huì)顯示表單的原始內(nèi)容。

    5. 從樹中提取值
    在應(yīng)用請(qǐng)求值階段,JSF 實(shí)現(xiàn)遍歷組件樹并用 decode 方法從請(qǐng)求中提取值,并在本地設(shè)置每一個(gè)組件。如果在這個(gè)過程中出現(xiàn)了任何錯(cuò)誤,那么它們就在 FacesContext 中排隊(duì)并在呈現(xiàn)響應(yīng)階段顯示給用戶。

    同時(shí),在這個(gè)階段排隊(duì)的所有由像單擊按鈕這樣的用戶操作產(chǎn)生的事件,都廣播給注冊(cè)的偵聽器。單擊 reset 按鈕會(huì)將文本字段中的值重新設(shè)置為它們?cè)瓉淼闹怠?/P>

    6. 處理驗(yàn)證
    在處理驗(yàn)證階段,對(duì)在應(yīng)用請(qǐng)求值階段設(shè)置的本地值進(jìn)行所有與各組件相關(guān)的驗(yàn)證。當(dāng) JSF 實(shí)現(xiàn)對(duì)每一個(gè)注冊(cè)的驗(yàn)證器調(diào)用 validate 方法時(shí)就會(huì)進(jìn)入此階段。

    如果任何一項(xiàng)驗(yàn)證失敗,那么生命周期就會(huì)進(jìn)入呈現(xiàn)響應(yīng)階段,在那里呈現(xiàn)帶有錯(cuò)誤信息的同一頁面。在這里,所有在這一階段排隊(duì)的事件同樣都會(huì)廣播給注冊(cè)的偵聽器。

    JSF 實(shí)現(xiàn)處理源字段上的驗(yàn)證器。如果數(shù)據(jù)是無效的,那么控制就交給呈現(xiàn)響應(yīng)階段,在這個(gè)階段重新顯示 FlightSearch.jsp 并帶有相關(guān)組件的驗(yàn)證錯(cuò)誤。通過在 JSP 頁面中聲明 output_errors, ,頁面中的所有錯(cuò)誤都會(huì)顯示在頁面的底部。

    7. 設(shè)置模型對(duì)象值
    在更新模型值階段,成功處理了所有驗(yàn)證后,JSF 實(shí)現(xiàn)就通過對(duì)每一組件調(diào)用 updateModel 方法用有效值設(shè)置模型對(duì)象值。如果在將本地?cái)?shù)據(jù)轉(zhuǎn)換為由模型對(duì)象屬性所指定的類型時(shí)出現(xiàn)任何錯(cuò)誤,那么生命周期就進(jìn)入呈現(xiàn)響應(yīng)階段,并將錯(cuò)誤顯示出來。來自表單字段屬性的值會(huì)填充為模型對(duì)象的屬性值。

    8. 可以調(diào)用 ActionListener
    可以將一個(gè) ActionListener 與一個(gè)用戶操作,如單擊提交按鈕相關(guān)聯(lián),如 清單 1所示。在調(diào)用應(yīng)用程序階段,對(duì) FlightSearchActionListener 調(diào)用了 processAction 方法。在實(shí)際應(yīng)用中, processAction 方法在調(diào)用后會(huì)搜索數(shù)據(jù)以找出滿足條件的航班,并從組件的 action 屬性中提取輸出。

    在本文提供的這個(gè)示例 Web 應(yīng)用程序中,我們使用了靜態(tài)數(shù)據(jù)表示航班表。這個(gè)方法還將提取的 action 屬性發(fā)送給 NavigationHandler 實(shí)現(xiàn)。 NavigationHandler 查詢 faces-config.xml 文件 -- 這是 JSF 的默認(rèn)應(yīng)用程序配置文件 -- 以根據(jù)這一輸出確定下一頁是什么。

    9. 呈現(xiàn)響應(yīng) 在呈現(xiàn)響應(yīng)階段,如果在 faces 上下文中沒有錯(cuò)誤,就顯示由查詢配置文件得到的這一頁 FlightList.jsp。如果是因?yàn)榍懊嫒我浑A段的錯(cuò)誤而到達(dá)這一階段的,那么就會(huì)重新顯示帶有錯(cuò)誤信息的 FlightSearch.jsp。

    圖 2. 處理一個(gè) JSF 請(qǐng)求
    單擊這里以觀看該圖。

    清單 1. FlightSearch.jsp,一個(gè)簡單的 JSF 表單
    
    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
    <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
    
    <f:use_faces>
     <h:form id="flightForm" formName="flightForm" >
       <h:input_text id="fromCity" valueRef="FlightSearchBean.fromCity">
      	<f:validate_required/>
       <h:input_text/>
    
       <h:input_text id="toCity" valueRef="FlightSearchBean.toCity">
       <h:input_text id="departureDate" 
         valueRef="FlightSearchBean.departureDate">
       <h:input_text id="arrivalDate" 
         valueRef="FlightSearchBean.arrivalDate">
    
       <h:command_button id="submit" action="success" 
         label="Submit" commandName="submit" >
           <f:action_listener 
             type="foo.bar.FlightSearchActionListener"/>
       </h:command_button>
       <h:command_button id="reset" action="reset" label="Reset" 
         commandName="reset" />
    
       <h:output_errors/>
     </h:form>
    </f:use_faces>
    

    在這段代碼中使用了兩個(gè) JSF-RI 的標(biāo)記庫。 html_basic 標(biāo)記庫定義了 HTML 組件常用的標(biāo)記,而 jsf-core 標(biāo)記庫包含用于注冊(cè)偵聽器和驗(yàn)證器的標(biāo)記。其他標(biāo)記有:

    • f:use_faces 標(biāo)記向 JSF 實(shí)現(xiàn)表明后面的標(biāo)記是 faces 標(biāo)記。
    • f:validate_required 標(biāo)記表明它所在的字段(在 FlightSearchBean 中是 fromCity 字段)在提交表單時(shí)應(yīng)該有值。
    • h:formh:input_text 標(biāo)記分別表示一個(gè)名為 flightSearchForm 的 HTML 表單和各種文本字段。
    • h:command_button 標(biāo)記用于表示提交和重設(shè)按鈕。
    • 最后, h:output_errors 標(biāo)記類似于 Struts html:errors 標(biāo)記,用于顯示在表單字段驗(yàn)證中出現(xiàn)的任何錯(cuò)誤。

    一個(gè)名為 FlightSearchBean 的 JavaBean 表示在更新模型值階段用 UIComponent 數(shù)據(jù)更新的模型。通常在 JSP 頁中 JavaBean 是用 jsp:useBean 標(biāo)記聲明的。您可能注意到了在 FlightSearch.jsp 中沒有這樣做。這是因?yàn)榭梢允褂?JSF 的一個(gè)名為 Managed Beans 的功能,在 faces 配置文件中聲明所有 JSP 頁面使用的 JavaBeans 組件。在開始時(shí),servlet 容器會(huì)初始化這些 JavaBeans 組件。faces-config.xml 文件中的 FlightSearchBean 入口如清單 2所示:

    清單 2. faces-config.xml 的 TravelInfoBean入口
    
    <managed-bean>
      <managed-bean-name>FlightSearchBean</managed-bean-name>
      <managed-bean-class>
        foo.bar.FlightSearchBean
      </managed-bean-class>
      <managed-bean-scope>session</managed-bean-scope>
    </managed-bean>
    

    現(xiàn)在讓我們看一看這些階段是如何處理響應(yīng)的。

    呈現(xiàn) faces 響應(yīng)
    一個(gè) faces 響應(yīng)是由 Faces 應(yīng)用程序在生成包含 JSF 標(biāo)記的 JSP 頁時(shí)生成的。這個(gè)響應(yīng)可以是 JSF 應(yīng)用程序的 faces 或者 non-faces 響應(yīng)。

    在我們的例子中,清單 1 中頁面的呈現(xiàn)是一個(gè) faces 響應(yīng)。您可能熟悉 Tag 接口的 doStartTag()doEndTag() 方法。在 JSF 和 Struts-Faces 中,每一個(gè)標(biāo)記都是從 UIComponentTag 擴(kuò)展的。 UIComponentTag 實(shí)現(xiàn)了 doStartTag()doEndTag() 方法。

    它還有兩個(gè)抽象方法 getComponentType()getRendererType()。 通過在具體的標(biāo)記類中實(shí)現(xiàn)這兩個(gè)方法,就可以分別指定組件和 renderer 的類型。

    考慮一個(gè)帶有文本字段的簡單 JSF 表單。在呈現(xiàn) JSF 表單時(shí)執(zhí)行以下一系列步驟。

    1. 調(diào)用 doStartTag() 方法
    Servlet 窗口對(duì) FormTag 調(diào)用 doStartTag() 方法。

    2. 得到 UIComponent
    FormTaggetComponentType() 方法得到其 UIComponent。 UIComponentTag ( FormTag 的父組件)使用 getComponentType() 以從 faces-config.xml 文件中查詢這個(gè)組件的類名,并創(chuàng)建 UIComponent(FormComponent )的一個(gè)實(shí)例。

    3. 得到 renderer
    下一步, FormTaggetRendererType 方法中得到其 renderer 。與組件類型一樣,renderer 名是在 faces-config.xml 文件中查詢的。

    4. 調(diào)用編碼方法
    在創(chuàng)建了 FormComponentFormRenderer 后,對(duì) FormComponent 調(diào)用 encodeBegin() 方法。每一個(gè)標(biāo)記的呈現(xiàn)都由 encodeBegin() 開始、由 encodeEnd() 結(jié)束。 encodeBegin() 方法是按嵌套的順序調(diào)用的。

    5. 結(jié)束標(biāo)記和呈現(xiàn) HTML
    servlet 容器對(duì)標(biāo)記調(diào)用 doEndTag() 方法。以嵌套的反順序?qū)γ恳粋€(gè)組件調(diào)用 encodeEnd() 方法。在最后,表單和所有嵌套的組件都呈現(xiàn)為 HTML。這時(shí),HTML 就生成完畢,并呈現(xiàn)出對(duì)應(yīng)于 JSP 的 HTML。

    圖 3 顯示構(gòu)成生成 faces 響應(yīng)的事件序列。

    圖 3. 呈現(xiàn)一個(gè) faces 響應(yīng)
    單擊這里以查看該圖。

    為什么將這三者集成為一體?
    隨著 JSP 和相關(guān)規(guī)范的不斷發(fā)展,像 JSF 和 JSP 標(biāo)記庫(或者 JSTL,它使用簡單的標(biāo)記封裝許多 JSP 應(yīng)用程序常用的核心功能)這樣的新標(biāo)準(zhǔn)正在不斷出現(xiàn)。下面是使用集成為一個(gè)整體的新技術(shù)一些好處:

    • 更清晰地分離行為和展示。 將標(biāo)記、 renderer 和組件分離,就可以更好地定義開發(fā)周期中的頁面作者和應(yīng)用程序開發(fā)人員的作用。
    • 改變一個(gè)組件的展示不會(huì)有雪崩效應(yīng)。現(xiàn)在您可以容易地只對(duì) renderer 作出改變。在傳統(tǒng)的 MVC 模型中,由于沒有這種分離,對(duì)于標(biāo)記的任何改變都需要改變業(yè)務(wù)邏輯。現(xiàn)在再不需要這樣了。
    • renderer 無關(guān)性。 也可以說是協(xié)議無關(guān)性,通過對(duì)帶有多個(gè) renderer 的多種展示設(shè)備重復(fù)使用組件邏輯實(shí)現(xiàn)。使用不同 renderer 的能力使得不再需要對(duì)特定的設(shè)備編寫整個(gè)表示層代碼。
    • 組裝和重用自定義組件的標(biāo)準(zhǔn)。JSF 的考慮范圍超出了“表單和字段”,它提供了豐富的組件模型用以呈現(xiàn)自定義 GUI 組件。用 JSF 可以定制每一個(gè)組件在頁面中的外觀和行為。開發(fā)人員還擁有創(chuàng)建他們自己的 GUI 組件(如菜單和樹)的能力,這些組件可以用簡單的自定義標(biāo)記容易地加入到任何 JSP 頁面中。就像 AWT 和 Swing 所提供的 Java 前端 GUI 組件一樣,我們可以在我們的 Web 頁而中有自定義的組件,它們使用自己的事件處理程序并有定制的外觀。這是 Web 層的 GUI 天堂!

    Struts 是一種已經(jīng)擁有大量客戶基礎(chǔ)的框架。許多 IT 部門認(rèn)識(shí)到這種 MVC 框架的價(jià)值并使用它有一段時(shí)間了。JSF 沒有像 Structs 這樣強(qiáng)大的控制器結(jié)構(gòu),也沒有像它那樣標(biāo)準(zhǔn)化的 ActionFormActions (及它們聲明的能力)。將 Tiles 集成到集合體中,就給了自己重復(fù)使用和以無縫的方式改變公司布局的能力。

    移植支持 JSF 的 Struts 應(yīng)用程序的挑戰(zhàn)是雙重的。首先,Struts 標(biāo)記不是 JSF 兼容的。換句話說,它們沒有像 JSF 規(guī)范所規(guī)定的那樣擴(kuò)展 UIComponentTag ,所以,JSF 不能解釋它們并關(guān)聯(lián)到 UIComponentRenderers

    其次,在 FacesServlet 與 Struts RequestProcessor 之間沒有鏈接。在 Struts 應(yīng)用程序中, RequestProcessor 負(fù)責(zé)用 ActionFormActions 類中的回調(diào)方法顯示。 ActionForm 屬性和 validate() 的 getter 和 setter 是 ActionForm 中的回調(diào)方法。對(duì)于 Actionexecute() 是回調(diào)方法。除非調(diào)用了 RequestProcessor ,否則 Struts ActionFormActions 類中的回調(diào)方法沒有機(jī)會(huì)調(diào)用業(yè)務(wù)邏輯。

    將 Struts 和 JSF 與 Struts-Faces 集成
    這里,您可能會(huì)問是否有軟件可以幫助將 Struts 與 JSF 集成,或者是否必須自己編寫集成軟件。

    好消息是已經(jīng)有這樣的軟件了。 Struts-Faces 是一個(gè)早期發(fā)布的 Struts JSF 集成庫。這個(gè)庫是由 Craig McClanahan 創(chuàng)建的,它使得將現(xiàn)有 Struts 應(yīng)用程序移植到 JSF 變得容易了(保留了對(duì)現(xiàn)有 Struts 投資的價(jià)值)。Struts-Faces 還力圖與 JSF 進(jìn)行簡潔的集成,這樣就可以在前端使用 JSF,同時(shí)后端仍然有熟悉的 Struts 組件。

    圖 4 展示了 Struts-Faces 與 JSF 類之間的關(guān)系。藍(lán)色的類屬于 Struts-Faces。

    圖 4. Struts-Faces 類圖
    單擊這里以查看該圖。

    下面是 Struts-Faces 的主要組件:

    • FacesRequestProcessor 類,它處理所有 faces 請(qǐng)求。這個(gè)類繼承了常規(guī) Struts RequestProcessor ,并處理 faces 請(qǐng)求。Non-faces 請(qǐng)求發(fā)送給出其父類 -- RequestProcessor
    • ActionListenerImpl 類,它處理像提交表單或者單擊鏈接這樣的 ActionEvent 。這個(gè)類用于代替由 JSF-RI 提供的默認(rèn) ActionListener 實(shí)現(xiàn)。只要在一個(gè) faces 請(qǐng)求中生成 ActionEvent ,就會(huì)對(duì) ActionListenerImpl 調(diào)用 processAction() 方法、并將 ActionEvents 轉(zhuǎn)送給 FacesRequestProcessor 。這很有意思,因?yàn)?RequestProcessor 通常只由 ActionServlet 調(diào)用以處理 HTTP 請(qǐng)求。
    • FormComponent 類,它擴(kuò)展了 JSF Form 組件,但是是在 Struts 生命周期內(nèi)調(diào)用的。
    • FormComponent 的 renderer 和標(biāo)記。
    • 只用于輸出的數(shù)據(jù)標(biāo)記和 renderer ,這里不需要分離組件。例如, ErrorsTagErrorsRenderer 用于在 HTML 中顯示表單錯(cuò)誤。
    • ServletContextListener 的名為 LifeCycleListener 的實(shí)現(xiàn)。它用于在初始化時(shí)注冊(cè)相應(yīng)的 RequestProcessor
    • faces-config.xml 文件。這個(gè)文件已經(jīng)捆綁在 struts-faces.jar 文件中。

    清單 3 展示了使用 Struts-Faces 標(biāo)記的 FlightSearch.jsp。它類似于在 清單 1中展示的 JSF 例子。這里用粗體突出了區(qū)別之處。在這里,您會(huì)發(fā)現(xiàn)增加了一個(gè)新標(biāo)記庫 tags-faces。這個(gè)標(biāo)記庫定義聲明這些標(biāo)記由 Struts-Faces API 所使用。

    清單 3. FlightSearch.jsp 使用 Struts-Faces 標(biāo)記
    
    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
    <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
    <%@ taglib uri="http://jakarta.apache.org/struts/tags-faces" 
      prefix="s" %> 
    
    <f:use_faces>
        <s:form action="/listFlights">
         <h:input_text id="fromCity" valueRef="FlightSearchForm.fromCity"/>
    
         <h:input_text id="toCity" valueRef="FlightSearchForm.toCity"/>
         <h:input_text id="departureDate" 
           valueRef="FlightSearchForm.departureDate">
         <h:input_text id="arrivalDate" 
           valueRef="FlightSearchForm.arrivalDate">
    
         <h:command_button id="submit" action="success" label="Submit" 
           commandName="submit" />
         <h:command_button id="reset" action="reset" label="Reset" 
           commandName="reset" />
    
         <s:errors/>
        </s:form>
    </f:use_faces>
    

    s:form 標(biāo)記用于創(chuàng)建這個(gè) HTML 表單。表單的 action 屬性是 /listFlights而不是像 清單 1那樣指定為表單名 flightForm。在 JSF 中,表單名只是指定給 UIForm 的名字而沒有更多的意義。

    FlightSearchBean 是 JSF 表單的模型,并在更新模型值階段得到其值。不過在 Struts 中,表單 action 指向 Struts 配置文件 struts-config.xml 中的 ActionMapping 。為了理解它是如何起作用的,還必須看一下清單 4 中顯示的 struts-config.xml 文件。

    您會(huì)看到 /listFlightsActionMapping 表明這個(gè) URI 路徑的 ActionFormfoo.bar.FlightSearchForm ,而 Action 類是 foo.bar.FlightSearchAction 。換句話說, ActionForm ( FlightSearchForm )本身就是 Struts-Faces 中的 HTML 表單的模型,它的 action 間接地指向這個(gè)模型(您可以在清單 3 中看到這一點(diǎn),那里文本字段標(biāo)記指向 FlightSearchForm 。在普通 Struts 應(yīng)用程序中這會(huì)是 <html:text property="fromCity"/> )。

    清單 4. 在 struts-config.xml 中聲明 Action
    
    <form-bean  name="FlightSearchForm"
                   type="foo.bar.FlightSearchForm"/>
    
    <!-- ========== Action Mapping Definition ========================= -->
    <action-mappings>
    
    <!-- List Flights action -->
     <action path="/listFlights"
        type="foo.bar.FlightSearchAction"
        name="FlightSearchForm"
        scope="request"
        input="/faces/FlightSearch.jsp">
        <forward name="success" path="/faces/FlightList.jsp"/>
     </action>
    
    </action-mappings>
    

    您會(huì)注意到在 action 屬性中缺少熟悉的 .do。這是因?yàn)?Struts-Faces 使用表單 action 本身作為表單名(它還應(yīng)該與 Struts 配置文件中的 ActionForm 名相匹配)。

    集成 Struts 和 Tiles 的五個(gè)步驟
    以下五步可以讓 Struts 1.1 和 Tiles 共同工作:
    1. 創(chuàng)建一個(gè) JSP 以表示站點(diǎn)的布局。這是主 JSP,并帶有頁頭、頁體和頁腳的占位符。分別用 Tiles 標(biāo)記添加到主 JSP 頁面中。
    2. 創(chuàng)建一個(gè) Tiles 定義文件并定義每個(gè)集成頁面的每個(gè)占位符中必須包括哪個(gè) JSP 頁面。用惟一的名稱標(biāo)識(shí)出每一個(gè)合成頁面定義。
    3. 在 struts-config.xml 文件中改變?nèi)趾捅镜剞D(zhuǎn)發(fā)以使用上一步驟中給出的惟一名稱而不是別名。
    4. 在啟動(dòng)時(shí)用 TilesPlugIn 裝載 Tiles 定義文件。將 TilesPlugIn 項(xiàng)加入到 struts-config.xml 文件中。
    5. 將 TilesRequestProcessor 項(xiàng)添加到 struts-config.xml 文件中。這是支持 Tiles 的 Struts 應(yīng)用程序的默認(rèn)請(qǐng)求處理程序。

    還要注意我們?cè)谶@里沒有使用 JSF validation 標(biāo)記。這是因?yàn)樵?Struts 中,驗(yàn)證是在 ActionForm 類中的 validate() 方法中進(jìn)行的,有可能是通過使用 Commons-Validator。 s:errors 標(biāo)記類似于 Struts 錯(cuò)誤標(biāo)記并用于顯示在驗(yàn)證時(shí)出現(xiàn)的錯(cuò)誤消息。

    另一件要注意的事情是沒有 ActionListener 顯式地與提交按鈕相關(guān)聯(lián)。這是因?yàn)樵?Struts-Faces 中已經(jīng)提供了 ActionListener 并且總是將 faces 請(qǐng)求與 ActionEvent s 一同轉(zhuǎn)交給 FacesRequestProcessor ,在那里根據(jù) struts-config.xml 文件將請(qǐng)求分派給相應(yīng)的 Action 類。

    將Struts 應(yīng)用程序移植到 JSF
    為了將 Struts Web 應(yīng)用程序與 JSF 集成,遵循以下步驟:

    • 將 struts-faces.jar 文件與特定于 JSF 的 JAR(jsf-api.jar、jsf-ri.jar) 添加到 Web 應(yīng)用程序的 WEB-INF/lib目錄中。
    • 如果準(zhǔn)備使用 JSF 和 JSTL,則將特定于 JSTL 的 JAR(jstl.jar、standard.jar)添加到 WEB-INF/lib 文件夾中。這一步只有在部署到常規(guī) Tomcat 時(shí)才會(huì)需要。JWSDP 已經(jīng)提供了這些 JAR。
    • 修改 Web 應(yīng)用程序部署描述符 ( /WEB-INF/web.xml)以便有一個(gè) Faces Servlet 項(xiàng), 如清單 5 所示。
    • 修改 JSP 頁面以使用 JSF 和 Struts-Faces 標(biāo)記而不是 Struts 標(biāo)記。特別是用 Struts-Faces 相應(yīng)標(biāo)記替換 html、b ase、 formerrors 標(biāo)記。用 JSF 相應(yīng)標(biāo)記替換 texttextarearadio 標(biāo)記。Struts-Faces 沒有單獨(dú)針對(duì)這些的標(biāo)記。盡管沒有要求,但是您可能還會(huì)考慮用 JSTL 標(biāo)記替換 Struts Logic 標(biāo)記。
    • 對(duì)于每一個(gè)使用 JSF 標(biāo)記的 JSP,修改 struts-config.xml 文件以在指向該 JSP 的 Action Mapping 中的 global-forwardslocal-forwards中加入前綴 /faces
    • 如果 Web 應(yīng)用程序使用了任何您創(chuàng)建的自定義組件,那么您就需要用 JSF 實(shí)現(xiàn)的默認(rèn) RenderKit 注冊(cè)它們。可以通過在 WEB-INF 文件中創(chuàng)建一個(gè) faces-config.xml 文件、并增加每一個(gè)組件和 renderer 的項(xiàng)做到這一點(diǎn)。不過,要記住 faces-config.xml 文件已經(jīng)綁定在 struts-faces.jar 文件中了。您必須從 struts-faces.jar 文件中提出它、加入自己的內(nèi)容并將它放到 WEB-INF文件夾中。
    清單 5. 在 web.xml 中聲明 FacesServlet
    
    <!-- JavaServer Faces Servlet Configuration -->
    <servlet>
    <servlet-name>faces</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    </servlet>
    
    <!-- JavaServer Faces Servlet Mapping -->
    <servlet-mapping>
      <servlet-name>faces</servlet-name>
      <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>
    

    集成 Struts-Faces 和 Tiles 的挑戰(zhàn)
    Struts-Faces 庫提供了 Struts 與 JSF 之間的一個(gè)高效的橋梁,使得在 J2EE Web 應(yīng)用程序中擁有豐富的表示層成為現(xiàn)實(shí)。您可以通過在組合體中添加 Titles 使表示層更豐富,這樣不僅得到了 Struts 和 JSF 組合的好處,而且還可以高效地重復(fù)使用不同的 JSP 頁面,因?yàn)樗鼈儗⒂煽梢愿鶕?jù)需要添加或者刪除的組件部分或者 tiles 所構(gòu)成。

    本文已經(jīng)展示了 Struts 和 JSP 的集成,您會(huì)想將 Tiles 加入到組合中只是小事一樁,是不是?

    不幸的是,JSF 仍然處于早期階段,還沒有給出最后的發(fā)布。基于這一考慮,Struts-Faces 集成軟件開發(fā)仍然在不斷地發(fā)展以包括 JSF 的不同的功能,并且還沒有支持 Tiles。

    Struts 和 Tiles 可以無縫地共同工作,但是在集成之路上您會(huì)遇到路障。在下面幾小節(jié)中,您會(huì)看到在與 Tiles 共同使用 Struts-Faces 集成庫時(shí)經(jīng)常遇到的問題的匯總。對(duì)于每一個(gè)問題,我們?cè)敿?xì)說明了一個(gè)修改 Struts-Faces 類的解決方案。我們將用一個(gè)航班搜索示例解釋這個(gè)解決方案。

    清單 6 展示了航班搜索頁面的布局。注意我們稱它為航班搜索頁面而不是 FlightSearch.jsp。這是因?yàn)?FlightSearch JSP 是用戶在 foobar 旅行 Web 站點(diǎn)看到的合成頁面的主體。

    現(xiàn)在,我們保持實(shí)際的 FlightSearch.jsp 不變。我們將隨著進(jìn)展改變它。在您這邊,也需要用航班搜索頁的定義創(chuàng)建一個(gè) Tiles 定義文件。清單 7(緊接著清單 6)展示了 Tiles 定義文件中航班搜索頁的一項(xiàng)。注意對(duì)帶有 extends 屬性的主布局模板的重復(fù)使用。

    在清單 6 和 7 后是每一個(gè)可能的挑戰(zhàn)。

    清單 6. 航班搜索例子的 Tiles 布局
    
    <%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
    <%@ taglib uri="http://jakarta.apache.org/struts/tags-faces"prefix="s" %>
    
    <!-- Layout component parameters: header, menu, body, footer -->
    <s:html>
    <head>
      <title> <tiles:getAsString name="title"/></title>
      <s:base/>
    </head>
    <body>
      <TABLE border="0" width="100%" cellspacing="5">
        <tr>
         <td><tiles:insert attribute="header"/></td>
        </tr>
    
        <tr>
         <td><tiles:insert attribute="body"/></td>
        </tr>
    
        <tr><td><hr></td></tr>
    
        <tr>
         <td><tiles:insert attribute="footer" /></td>
        </tr>
      </TABLE>
    </body>
    </s:html>
    

    清單 7. 航班搜索頁的 Tiles 定義
    
    <!-- Master Layout definition  -->
    <definition name="foobar.master-layout" 
      path="/faces/layout/MasterLayout.jsp">
          <put name="title"  value="Welcome to Foo Bar Travels" />
          <put name="header" value="/faces/common/header.jsp" />
          <put name="footer" value="/faces/common/footer.jsp" />
          <put name="body"   value="" />
    </definition>
    
      <!-- Definition for Flight Search Page -->
    <definition name="/foobar.flight-search" 
      extends="foobar.master-layout">
          <put name="body"   value="/faces/FlightSearch.jsp" />
    </definition>

    響應(yīng)已經(jīng)提交
    這是您在試圖訪問航班搜索表單時(shí)馬上會(huì)看到的第一個(gè)問題。小心查看堆棧跟蹤。您會(huì)看到問題出在類 com.sun.faces.lifecycle.ViewHandlerImpl 上。這是一個(gè)實(shí)現(xiàn)了 ViewHandler 接口的 JSF-RI 類。

    圖 2展示了 ViewHandler 所扮演的角色。這是一個(gè)將請(qǐng)求轉(zhuǎn)發(fā)給下一頁的類。在轉(zhuǎn)發(fā)請(qǐng)求時(shí),它不在轉(zhuǎn)發(fā)前檢查響應(yīng)的狀態(tài) -- 這只有在使用 Tiles 時(shí)才會(huì)發(fā)生,因?yàn)?Tiles 內(nèi)部將 JSP 頁面包括在響應(yīng)內(nèi),而 JSF-RI 在第一次轉(zhuǎn)發(fā)后提交響應(yīng)、然后試圖再次轉(zhuǎn)發(fā)給下面的包括 JSP 的 Tiles。

    要解決這個(gè)問題,必須創(chuàng)建一個(gè)自定義的 ViewHandler 實(shí)現(xiàn),它將檢查響應(yīng)的狀態(tài)以確定它是否提交過。如果響應(yīng)沒有提交過,那么請(qǐng)求就轉(zhuǎn)發(fā)給下一頁,否則,就加入請(qǐng)求并顯示相應(yīng)的 JSP。我們將創(chuàng)建一個(gè)名為 STFViewHandlerImpl 的類,它實(shí)現(xiàn)了 ViewHandler 接口并實(shí)現(xiàn)了所需要的方法 renderView()。 清單 8 展示了 STFViewHandlerImpl 中的 renderView() 方法:

    清單 8. STFViewHandlerImpl 中的 renderView()方法
    
    RequestDispatcher rd = null;
    Tree tree = context.getTree();
    String requestURI = context.getTree().getTreeId();
    rd = request.getRequestDispatcher(requestURI);
    
    /** If the response is committed, include the resource **/
    if( !response.isCommitted() ) {
       rd.forward(request, context.getServletResponse());
    }
    else {
       rd.include(request, context.getServletResponse());
    }

    現(xiàn)在您實(shí)現(xiàn)了自己的 ViewHandler ,如何通知 JSF-RI 使用您的 ViewHandler 而不是默認(rèn)的實(shí)現(xiàn)呢?要回答這個(gè)問題,就必須理解 FacesServlet 的工作過程。

    在 Faces 初始化過程中, FacesServlet 會(huì)讓 LifecycleFactory 實(shí)現(xiàn)返回 Lifecycle 類的一個(gè)實(shí)現(xiàn),如清單 9 所示:

    清單 9. FacesServlet 中 Faces 的初始化
    
    //Get the LifecycleFactory from the Factory Finder
    LifecycleFactory factory = (LifecycleFactory) 
      FactoryFinder.getFactory("javax.faces.lifecycle.LifecycleFactory");
    
    //Get the context param from web.xml
    String lifecycleID = 
    getServletContext().getInitParameter("javax.faces.lifecycle.LIFECYCLE_ID");
    
    //Get the Lifecycle Implementation
    Lifecycle lifecycle = factory.getLifecycle(lifeCycleID);
    

    Lifecycle 實(shí)現(xiàn)對(duì)象擁有在呈現(xiàn)響應(yīng)階段要使用的 ViewHandler 。您可以通過對(duì) Lifecycle 實(shí)現(xiàn)調(diào)用 setViewHandler 方法讓自己的 ViewHandler 實(shí)現(xiàn)成為默認(rèn)的。

    現(xiàn)在問題變?yōu)槿绾蔚玫侥J(rèn) Lifecycle 實(shí)現(xiàn)?回答是不需要這樣做。只要?jiǎng)?chuàng)建一個(gè)新的實(shí)現(xiàn)并用一個(gè)惟一 ID 注冊(cè)它,如清單 10 所示:

    清單 10. 注冊(cè)自定義 ViewHandler 和 Lifecycle
    
    //Get the LifecycleFactory from the Factory Finder
    LifecycleFactory factory = (LifecycleFactory) 
      FactoryFinder.getFactory("javax.faces.lifecycle.LifecycleFactory");
    
    //Create a new instance of Lifecycle implementation - 
    //com.sun.faces.lifecycle.LifecycleImpl
    //According to the documentation, factory.getLifecycle("STFLifecycle") 
    //should work, but JSF-RI has a defect.
    //Hence this workaround of creating a RI class explicitly.
    LifecycleImpl stfLifecycleImpl = new LifecycleImpl();
    
    //Create a new instance of our STFViewHandler and set it on the Lifecycle
    stfLifecycleImpl.setViewHandler(new STFViewHandlerImpl());
    
    //Register the new lifecycle with the factory with a unique 
    //name "STFLifecycle"
    factory.addLifecycle("STFLifecycle", stfLifecycleImpl);
    

    您可以看到 lifecycleId 硬編碼為 STFLifecycle 。實(shí)際上不是這樣。當(dāng)您回過頭分析 清單 9時(shí)就會(huì)清楚。 FacesServlet 從在 web.xml 文件中聲明的上下文參數(shù)中得到名為 javax.faces.lifecycle.LIFECYCLE_ID 的 lifecycle ID,如下所示:

    
        <context-param>
            <param-name>javax.faces.lifecycle.LIFECYCLE_ID</param-name>
            <param-value>STFLifecycle</param-value>
        </context-param>
    

    因?yàn)?FacesServlet 取決于其初始化時(shí)的 Lifecycle 實(shí)現(xiàn),在 清單 10中展示的代碼應(yīng)該在 FacesServlet 初始化之前執(zhí)行。通過創(chuàng)建另一個(gè) servlet 并在 FacesServlet 之前初始化它而做到這一點(diǎn)。

    但是一種更聰明的辦法是實(shí)現(xiàn)一個(gè) ServletContextListener 接口。這個(gè)類聲明兩個(gè)方法: contextInitialized()contextDestroyed() ,在 Web 應(yīng)用程序被創(chuàng)建及 Web 應(yīng)用程序被銷毀之前會(huì)分別調(diào)用它們。因而 清單 10中的代碼在 contextInitialized() 方法中執(zhí)行,而自定義 ViewHandler 已經(jīng)用標(biāo)識(shí)名 STFLifecycle 注冊(cè)到 Lifecycle ,并且可被 FacesServlet 使用。 ServletContextListener 類本身是在 web.xml 文件中聲明的,如下所示:

    
    <listener>
      <listener-class>foo.bar.stf.application.STFContextListener
      </listener-class>
    </listener>
    

    這不是注冊(cè)一個(gè)帶有自定義 ViewHandlerLifecycle 惟一方法。事實(shí)上 FactoryFinder 實(shí)現(xiàn)了自己的發(fā)現(xiàn)算法以發(fā)現(xiàn) Factory 對(duì)象,包括 LifecycleFactory 。這些機(jī)制按照順序包括在系統(tǒng)屬性中查看工廠實(shí)現(xiàn)類名的機(jī)制、faces.properties file、或者 1.3 Services 發(fā)現(xiàn)機(jī)制( META-INF/services/{factory-class-name} )。不過,我們討論的這種機(jī)制是最容易的,也是最不具有破壞性的一種。

    404 Resource Not Found
    在解決了提交響應(yīng)的問題后,單擊任何一個(gè) Tiles 特定的鏈接或者輸入一個(gè)會(huì)呈現(xiàn) Faces 響應(yīng)的 URL。在這里,可以輸入顯示 FlightSearchForm 的 URL。

    在這樣做了以后,您會(huì)得到一個(gè) foobar.flight-search - 404 Resource Not Found 錯(cuò)誤。 foobar.flight-search 是航班搜索頁面的 Tiles 定義的名字。 FacesRequestProcessor 不能處理 Tiles 請(qǐng)求(因?yàn)樗鼣U(kuò)展的是 RequestProcessor 而不是 TilesRequestProcessor ),所以會(huì)得到錯(cuò)誤。

    為解決這個(gè)問題,我們將創(chuàng)建一個(gè)名為 STFRequestProcessor (表示 Struts-Tiles-Faces Request Processor)的新的請(qǐng)求處理程序。現(xiàn)在我們將拷貝 FacesRequestProcessor 的所有代碼到這個(gè)新類中。惟一的區(qū)別是 STFRequestProcessor 繼承的是 TilesRequestProcessor 而不是繼承常規(guī)的 RequestProcessor 。這個(gè)新的 RequestProcessor 可以處理 Tiles 請(qǐng)求。清單 11 詳細(xì)列出了這個(gè) STFRequestProcessor

    清單 11. STFRequestProcessor.java

    正如您所知道的, Struts 框架的 RequestProcessor 是在 struts-config.xml 文件中指定的。將下面的項(xiàng)添加到 struts-cinfig.xml 文件中后, STFRequestProcessor 就成為處理程序:

    
    <controller processorClass="foobar.stf.application.STFRequestProcessor" />
    

    表單提交顯示返回同一個(gè)表單
    由于 STFRequestProcessor 的作用,這時(shí)您就可以瀏覽并查看航班頁面了。不過,在提交航班搜索表單時(shí),您會(huì)得到返回來的同一個(gè)表單,而且沒有頁頭和頁腳!并且沒有驗(yàn)證錯(cuò)誤。事實(shí)上,根本就沒有進(jìn)行驗(yàn)證!

    為了了解到底發(fā)生了什么事情,我們用瀏覽器回到航班頁面并檢查 HTML 源代碼。您會(huì)看到像下面這樣的一項(xiàng):

    
    <form name="FlightSearchForm" method="post" 
      action="/flightapp/faces/FlightSearch.jsp">
    

    注意表單 action 是指向 JSP 頁而不是一個(gè) .do 的。啊哈!這就是問題!這不是由于同時(shí)使用 Tiles 和 Struts-Faces 而帶來的新問題,Struts-Faces 的默認(rèn)行為是讓 JSP 與表單 action 有同樣的名字。這種行為在有單一的 JSP 頁(如在前面的 Struts-Faces 例子中)時(shí)沒有問題。 清單 3展示了原來的 FlightSearch.jsp,讓我們繼續(xù)并像下面這樣修改 action:

    
    <s:form action="/listFlights.do>
    

    當(dāng)然,光有這種修改并不能解決問題。作了這種改變后,您就會(huì)發(fā)現(xiàn) STFRequestProcessor 不能找到 ActionForm 。顯然還需要其他的改變。

    不過,在繼續(xù)往下之前,看一下圖&#160 5。它顯示了在呈現(xiàn)負(fù)責(zé) Struts-Faces 表單的 faces 時(shí)相關(guān)的一系列事件。這與 圖 3相同,除了在 FormComponent 中突出顯示的方法 createActionForm()。 由 Struts-Faces API 提供的 FormComponent 類是 javax.faces.component.UIForm 的特殊子類,它支持請(qǐng)求或者會(huì)話范圍的表單 Bean。

    圖 5. 呈現(xiàn) Struts-Faces 響應(yīng)
    單擊這里以查看該圖。

    正如您所看到的, createActionForm() 方法使用 action 名以從 Struts 配置文件中得到 ActionMapping 。因?yàn)闆]有對(duì)于 /listFlights.doActionMapping ,所以 Struts 不能找到 ActionForm。

    這個(gè)問題的解決方法是使用 org.apache.struts.util.RequestUtilsRequestUtils 中的 static 方法 getActionMappingName() 具有足夠的智能解析映射到正確 ActionMapping 的路徑( /x/y/z)或者后綴( .do)。

    清單 12 以粗體顯示對(duì) createActionForm 方法的改變。我們沒有對(duì) Struts-Faces 中的 FormComponent 作這些改變,而是通過繼承 FormComponent 并覆蓋 createActionForm() 方法創(chuàng)建了一個(gè)新的 STFFormComponent。

    清單 12. FormComponent 中修改過的 createActionForm() 方法
    
    // Look up the application module configuration information we need
    ModuleConfig moduleConfig = lookupModuleConfig(context);
    
    // Look up the ActionConfig we are processing
    String action = getAction();
    String mappingName = RequestUtils.getActionMappingName(action);
    ActionConfig actionConfig = moduleConfig.findActionConfig(mappingName);
    ....
    ....
    

    對(duì)新的 STFFormComponent 還要作一項(xiàng)改變。Struts-Faces 將 action 名本身作為表單名。這需要改變,因?yàn)?action 帶有后綴 .do,而表單名沒有后綴 .do。所以我們?cè)?STFFormComponent 上增加一個(gè)名為 action 的新屬性,并覆蓋 getAction()setAction() 方法。

    FormRenderer 的改變
    必須對(duì) FormRenderer (以 HTML 格式呈現(xiàn) Struts-Faces 表單的類)的 encodeBegin 方法進(jìn)行類似于 清單 10所示的修改。

    同樣,通過繼承 FormRenderer 做到這一點(diǎn)。此外,還必須改變寫出到 HTML 的表單 action。清單 13以粗體詳細(xì)列出了這些改變:

    清單 13. FormRenderer 的改變
    
    protected String action(FacesContext context, UIComponent component) {
    
        String treeId = context.getTree().getTreeId();
        StringBuffer sb = new StringBuffer 
          (context.getExternalContext().getRequestContextPath());
        sb.append("/faces");
    
        // sb.append(treeId); -- This is old code, replaced with 
        // the two lines below.
    
        STFFormComponent fComponent = (STFFormComponent) component;
        sb.append(fComponent.getAction());
        
        return (context.getExternalContext().encodeURL(sb.toString()));
    }
    

    FormTag的改變
    正如您已經(jīng)知道的,當(dāng)組件和 renderer 改變時(shí),標(biāo)記也必須改變。在這里,通過繼承 Struts-Faces 中的 FormTag 創(chuàng)建一個(gè)新的標(biāo)記: STFFormTag 。不必改變?nèi)魏喂δ埽灰采w getComponentType()getRendererType() 方法。清單 14 展示了從 STFFormComponent 覆蓋的方法:

    清單 14. FormTag 的改變
    
    public String getComponentType()
    {
        return ("STFFormComponent");
    }
    
    public String getRendererType()
    {
        return ("STFFormRenderer");
    }
    

    修改 faces-config.xml 文件
    自定義組件和 renderer 必須在 faces-config.xml 文件中聲明,這樣 JSF 框架才可以初始化并使用它們。現(xiàn)在我們已經(jīng)創(chuàng)建了一個(gè)新組件 STFFormComponent 和一個(gè)新 renderer STFFormRenderer

    現(xiàn)在我們將在 faces-config.xml 文件中增加一個(gè)聲明,如清單 15 所示。 component-class 是組件的完全限定類名。 component-type 指的是在 STFFormTag ( 清單 12)中用于標(biāo)識(shí)組件的名字。以類似的方式發(fā)現(xiàn)和解釋 renderer。注意 faces-config.xml 文件是在 struts-faces.jar 文件中的。從 struts-faces.jar 文件中取出這個(gè)文件并將它放到 Web 應(yīng)用程序的 WEB-INF文件夾中并修改它。

    清單 15. 在 faces-config.xml 中聲明自定義組件和 renderer
    
    <faces-config>
    
      <!-- Custom Components -->
      <component>
        <component-type>STFFormComponent</component-type>
        <component-class>
          foobar.stf.component.STFFormComponent
        </component-class>
      </component>
      ..
      ..
      ..
      <!-- Custom Renderers -->
      <render-kit>
    
        <renderer>
          <renderer-type>STFFormRenderer</renderer-type>
          <renderer-class>
            foobar.stf.renderer.STFFormRenderer
          </renderer-class>
        </renderer>
        ..
        ..
        ..
      </render-kit>
    </faces-config>
    

    修改 struts-faces.tld 文件
    您不會(huì)在這個(gè)示例 Struts-Faces 應(yīng)用程序中看到 struts-faces.tld 文件,它打包到了 struts-faces.jar 文件中。打開并分析這個(gè)文件。它聲明了一個(gè)名為 org.apache.struts.faces.taglib.LifecycleListener 的類,這個(gè)類實(shí)現(xiàn)了 ServletContextListener 并初始化 FacesRequestProcessor

    因?yàn)橄M褂眯碌?STFRequestProccessor ,所以必須將這個(gè)文件從 struts-faces.jar 文件中刪除,將它放到 Web 應(yīng)用程序的 WEB-INF 文件夾中,并刪除偵聽器聲明。如果讓這個(gè) tld 文件保持原樣,那么在初始化這個(gè) Web 應(yīng)用程序時(shí),除了 STFRequestProcessor ,還會(huì)實(shí)例化一個(gè) FacesRequestProcessor。

    修改 base href 標(biāo)記
    現(xiàn)在,您已經(jīng)完成了 Struts、Tiles、JSF 集成的最困難的部分了。您甚至可以瀏覽航班搜索頁面,并輸入搜索標(biāo)準(zhǔn)查看航班列表。現(xiàn)在試著從航班列表頁面返回航班搜索表單。您會(huì)得到一個(gè) HTTP 400 錯(cuò)誤。這個(gè)錯(cuò)誤的原因是 HTML base href 標(biāo)記。它被設(shè)置為 Master Layout 頁面。

    
    <base href=
      "http://localhost:8080/stf-example/faces/layout/MasterLayout.jsp" />
               |_________|       |_____________________|
                  Context               Servlet Path
    

    程序所有頁面瀏覽都是相對(duì)于布局頁面計(jì)算的。如果加入的 base href 標(biāo)記只達(dá)到 Web 應(yīng)用程序上下文則會(huì)很方便,像這樣:

    
    <base href="http://localhost:8080/stf-example/" />
    

    我們可以通過定制 Struts-Faces BaseTag 做到這一點(diǎn)。這個(gè)類中的改變相當(dāng)微不足道。只須在 base href 中去掉 HttpServletRequest.getServletPath()

    因?yàn)檫@些改變是與顯示相關(guān)的,所以為它創(chuàng)建了一個(gè)名為 STFBaseRenderer 的新 renderer。這個(gè)新標(biāo)記稱為 STFBaseTag ,它聲明 STFBaseRenderer 作為其關(guān)聯(lián)的 renderer。不需要新的組件。

    有了這些信息,通過繼承 BaseTag 并覆蓋 getRendererType 方法創(chuàng)建新的 STFBaseTag ,如下所示:

    
    public String getRendererType()
    {
        return ("STFBaseRenderer");
    }
    

    到目前為止所作的改變
    恭喜!經(jīng)過這些相對(duì)較小的修改,您已經(jīng)成功地集成了 Struts、Tiles 和 JSF,并保留了您以前在這些技術(shù)上所做的所有投資。本文演示了如何將 JSF 強(qiáng)大的前端能力、 Tiles 的內(nèi)容格式編排優(yōu)勢(shì)以及 Struts 控制器層的靈活性結(jié)合在一個(gè)包中,使得創(chuàng)建一個(gè) J2EE Web 應(yīng)用程序成為一項(xiàng)更容易的任務(wù)。

    我們討論了定制 Struts 類以便與 JavaServer Faces 和 Tiles 框架形成緊密集成的工作關(guān)系,包括下面這些修改和增加:

    • 新的 ViewHandler ,用于檢查提交的響應(yīng)。
    • 新的 ServletContextListener ,用于創(chuàng)建新的 Lifecycle 實(shí)現(xiàn)并注冊(cè)這個(gè)定制的 ViewHandler。
    • 新的 RequestProcessor ,用于處理 Tiles 請(qǐng)求。
    • 修改過的 web.xml 文件,聲明新的 ServletContextListener 和 JSF Lifecycle ID。
    • 新的 FormTag、 FormComponentFormRenderer 類。
    • 新的 BaseTagBaseRenderer 類。
    • 修改過的 faces-config.xml 文件,它聲明了新的組件和 renderer。
    • 修改過的 struts-faces.tld 文件,不聲明偵聽器。

    希望它可以概括本文中使用的復(fù)合技術(shù),最重要的是,我們?yōu)槟峁┝藢?Struts、Tiles 和 JavaServer Faces 結(jié)合到用于構(gòu)建 Web 應(yīng)用程序的一個(gè)強(qiáng)大而靈活的機(jī)制中的一個(gè)令人信服的路線圖。

    參考資料

    • 您可以參閱本文在 developerWorks 全球站點(diǎn)上的 英文原文.

    • 下載本文的 例子和代碼,并遵循 README.txt 中給出的編譯和部署的說明。

    • Ant 用于對(duì)例子進(jìn)行編譯,可以從 Apache Ant 項(xiàng)目Web 站點(diǎn)下載它。

    • 有關(guān) Struts 和 Tiles 的更多內(nèi)容,包括可下載的教程、文檔、二進(jìn)制文件和源代碼,可從 Apache Jakarta Project StrutsWeb 站點(diǎn)獲得。

    • 可以將 JSF Early Acess Release 4 (EA4) 作為 Java Web Services Developer Pack Version 1.2 的一部分下載 -- 它帶有自己版本的 Tomcat。

    • 可以從 Jakarta 站點(diǎn)下載 Struts-Faces integration library的 0.3 和 0.4 版本。

    • 可以從 Java Web Services Developer Pack 1.2 下載 JSF-RI。

    • Struts, an open-source MVC implementation”( developerWorks, 2001年2月)介紹了 Struts,這是一個(gè)使用了 servlets 和 JavaServer Pages 技術(shù)的模型-視圖-控制器實(shí)現(xiàn)。

    • Struts and Tiles aid component-based development”( developerWorks,2002年6月)解釋了為什么結(jié)合 Struts 和 Tiles 可以成為創(chuàng)建 Web 應(yīng)用程序的出色軟件包,并展示了如何使用它,側(cè)重于 Struts 0.9 之后的改變。

    • Struttin' your stuff with WebSphere Studio Application Developer, Part 2: Tiles” ( developerWorks,2002年11月)是一個(gè)教程,主要關(guān)注在使用 WebShpere Studio Application Developer 作為開發(fā)環(huán)境時(shí)結(jié)合 Struts 使用 Tiles 模型框架。

    • Architect Struts applications for Web services”( developerWorks,2003年4月)展示了如何基于 MVB 設(shè)計(jì)模式用 Struts 建立 Web 服務(wù)應(yīng)用程序。

    • A JSTL primer”( developerWorks,2003年2-5月),這是一個(gè)分為四部分的系列,提供了有關(guān) JSTL 的所有內(nèi)容,包括如何使用 JSTL 標(biāo)記以避免在 JSP 頁面中使用腳本元素、如何通過刪除表示層中的代碼簡化軟件維護(hù)、以及 JSTL 的簡化的表達(dá)式語言,它使得無需使用全功能的編程語言就可以為 JSTL action 指定動(dòng)態(tài)屬性值。

    • 學(xué)習(xí)用 JSF 開發(fā) Web 應(yīng)用程序的基本內(nèi)容。在其教程“ UI development with JavaServer Faces” ( developerWorks,2003年9月)中,Jackwind Li Guojie 探討了 JSF 生命周期、輸入驗(yàn)證、事件處理、頁面瀏覽和國際化。

    • Sun 的 JSF Web 站點(diǎn)是另一個(gè)很好的學(xué)習(xí)有關(guān) JavaServer Faces 技術(shù)的起點(diǎn)。

    • ServerSide.com J2EE 社區(qū)是查找有關(guān) J2EE 的資源及參加開發(fā)者論壇的理想地點(diǎn)。

    • Java Community Process站點(diǎn)可以迅速得到有關(guān) JavaServer Pages 1.2 規(guī)范的內(nèi)容。

    • developerWorks Java 技術(shù)專區(qū) 可以找到關(guān)于 Java 編程各方面的數(shù)百篇文章。
    posted on 2005-11-10 00:19 閔毓 閱讀(720) 評(píng)論(0)  編輯  收藏 所屬分類: Java開發(fā)Struts in action
    主站蜘蛛池模板: 免费看美女让人桶尿口| 亚洲a∨无码精品色午夜| 四虎影在线永久免费观看| 99久9在线|免费| 国产精品免费视频观看拍拍| 亚洲综合色区中文字幕| 亚洲va无码va在线va天堂| 亚洲国产激情一区二区三区| 大地资源二在线观看免费高清| 精品成人免费自拍视频| 又长又大又粗又硬3p免费视频| 亚洲欧洲AV无码专区| 亚洲伊人久久精品| 亚洲视频中文字幕| 情人伊人久久综合亚洲| 亚洲中文字幕丝袜制服一区| 日本免费无遮挡吸乳视频电影| 国产va免费精品观看精品| 91精品手机国产免费| 一级毛片不卡片免费观看| 最近的2019免费中文字幕| 亚洲天堂免费在线视频| 一级**爱片免费视频| 黄色三级三级三级免费看| 亚洲AV无码一区二区一二区| 亚洲色偷偷综合亚洲AV伊人蜜桃| 亚洲日韩中文字幕天堂不卡| 亚洲系列国产精品制服丝袜第| 亚洲大片在线观看| 亚洲国产精品热久久| 久久91亚洲精品中文字幕| 亚洲AV成人片色在线观看高潮 | 久久亚洲精品中文字幕无码| 亚洲自偷自偷图片| 亚洲国产成人一区二区三区| 亚洲色成人中文字幕网站| 国产亚洲AV无码AV男人的天堂| 国产精品亚洲精品日韩已满| 亚洲国产精品无码久久久不卡 | 日本精品久久久久久久久免费| 国产成人人综合亚洲欧美丁香花|