<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)勢和 Struts controller 層的靈活性都加入到您的J2EE Web 應(yīng)用程序中?企業(yè)級 Java 專家 Srikanth Shenoy 和 Nithin Mallya 為您展示了如何將這三者的功能集成到一起。本文演示了如何在 Struts-Faces集成庫中定制類以使得它們可以與 Tiles 和 JSF 一同使用,并用一個實(shí)際的例子解釋了這個過程背后的基本原理以及如何使用新的一組類的細(xì)節(jié)。

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

    Struts 框架推出已經(jīng)有一段時間了,它已經(jīng)成為在開發(fā) J2EE Web 應(yīng)用程序時開發(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 中有一些功能是重疊的,但是它們在其他方面起到了互為補(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é)上。首先有個壞消息:在本文發(fā)表的時候,這三種技術(shù)是不能直接互操作的。好消息是:在本文中,我們展示了集成 Struts、Tiles 和 JSF 的方法。我們假設(shè)您已經(jīng)了解 Struts 和 Tiles。對 JSF 有一些了解會有幫助(參閱 參考資料中提供的 developerWorks 上的 JSF 教程的鏈接),但是不了解也不妨礙對本文的理解。

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

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

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

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

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

    JSF 和 UI 組件
    您可能已經(jīng)用 Java AWT 或者 Swing API 創(chuàng)建過 Java GUI 應(yīng)用程序,所以您應(yīng)該熟悉 JSF 的 UIComponent (它與 AWT 或者 Swing 組件很相像)。它儲存其子組件的樹(如果有的話)并為客戶端發(fā)生的動作生成標(biāo)準(zhǔn)事件,例如單擊一個按鈕以提交表單。這些事件緩存在 FacesContext 中。您可以用自定義標(biāo)記關(guān)聯(lián)每一個這種事件的處理程序。例如,用一個自定義的 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 頁面。這個 UI 組件運(yùn)行在服務(wù)器上,并用適當(dāng)?shù)?renderer ( MyRenderer )以 HTML 的形式呈現(xiàn)給客戶。這個 JSP 頁面表現(xiàn)了在 JSF-RI 中使用帶自定義標(biāo)記的用戶界面組件而不是在 HTML 中對它們進(jìn)行編碼。

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

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

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

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

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

    JSF 生命周期有六個階段:

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

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

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

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

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

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

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

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

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

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

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

    同時,在這個階段排隊的所有由像單擊按鈕這樣的用戶操作產(chǎn)生的事件,都廣播給注冊的偵聽器。單擊 reset 按鈕會將文本字段中的值重新設(shè)置為它們原來的值。

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

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

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

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

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

    在本文提供的這個示例 Web 應(yīng)用程序中,我們使用了靜態(tài)數(shù)據(jù)表示航班表。這個方法還將提取的 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 上下文中沒有錯誤,就顯示由查詢配置文件得到的這一頁 FlightList.jsp。如果是因?yàn)榍懊嫒我浑A段的錯誤而到達(dá)這一階段的,那么就會重新顯示帶有錯誤信息的 FlightSearch.jsp。

    圖 2. 處理一個 JSF 請求
    單擊這里以觀看該圖。

    清單 1. FlightSearch.jsp,一個簡單的 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>
    

    在這段代碼中使用了兩個 JSF-RI 的標(biāo)記庫。 html_basic 標(biāo)記庫定義了 HTML 組件常用的標(biāo)記,而 jsf-core 標(biāo)記庫包含用于注冊偵聽器和驗(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 字段)在提交表單時應(yīng)該有值。
    • h:formh:input_text 標(biāo)記分別表示一個名為 flightSearchForm 的 HTML 表單和各種文本字段。
    • h:command_button 標(biāo)記用于表示提交和重設(shè)按鈕。
    • 最后, h:output_errors 標(biāo)記類似于 Struts html:errors 標(biāo)記,用于顯示在表單字段驗(yàn)證中出現(xiàn)的任何錯誤。

    一個名為 FlightSearchBean 的 JavaBean 表示在更新模型值階段用 UIComponent 數(shù)據(jù)更新的模型。通常在 JSP 頁中 JavaBean 是用 jsp:useBean 標(biāo)記聲明的。您可能注意到了在 FlightSearch.jsp 中沒有這樣做。這是因?yàn)榭梢允褂?JSF 的一個名為 Managed Beans 的功能,在 faces 配置文件中聲明所有 JSP 頁面使用的 JavaBeans 組件。在開始時,servlet 容器會初始化這些 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)
    一個 faces 響應(yīng)是由 Faces 應(yīng)用程序在生成包含 JSF 標(biāo)記的 JSP 頁時生成的。這個響應(yīng)可以是 JSF 應(yīng)用程序的 faces 或者 non-faces 響應(yīng)。

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

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

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

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

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

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

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

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

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

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

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

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

    Struts 是一種已經(jīng)擁有大量客戶基礎(chǔ)的框架。許多 IT 部門認(rèn)識到這種 MVC 框架的價值并使用它有一段時間了。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)方法。對于 Actionexecute() 是回調(diào)方法。除非調(diào)用了 RequestProcessor ,否則 Struts ActionFormActions 類中的回調(diào)方法沒有機(jī)會調(diào)用業(yè)務(wù)邏輯。

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

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

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

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

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

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

    清單 3 展示了使用 Struts-Faces 標(biāo)記的 FlightSearch.jsp。它類似于在 清單 1中展示的 JSF 例子。這里用粗體突出了區(qū)別之處。在這里,您會發(fā)現(xiàn)增加了一個新標(biāo)記庫 tags-faces。這個標(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)建這個 HTML 表單。表單的 action 屬性是 /listFlights而不是像 清單 1那樣指定為表單名 flightForm。在 JSF 中,表單名只是指定給 UIForm 的名字而沒有更多的意義。

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

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

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

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

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

    另一件要注意的事情是沒有 ActionListener 顯式地與提交按鈕相關(guān)聯(lián)。這是因?yàn)樵?Struts-Faces 中已經(jīng)提供了 ActionListener 并且總是將 faces 請求與 ActionEvent s 一同轉(zhuǎn)交給 FacesRequestProcessor ,在那里根據(jù) struts-config.xml 文件將請求分派給相應(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 時才會需要。JWSDP 已經(jīng)提供了這些 JAR。
    • 修改 Web 應(yīng)用程序部署描述符 ( /WEB-INF/web.xml)以便有一個 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ú)針對這些的標(biāo)記。盡管沒有要求,但是您可能還會考慮用 JSTL 標(biāo)記替換 Struts Logic 標(biāo)記。
    • 對于每一個使用 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 注冊它們。可以通過在 WEB-INF 文件中創(chuàng)建一個 faces-config.xml 文件、并增加每一個組件和 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 之間的一個高效的橋梁,使得在 J2EE Web 應(yīng)用程序中擁有豐富的表示層成為現(xiàn)實(shí)。您可以通過在組合體中添加 Titles 使表示層更豐富,這樣不僅得到了 Struts 和 JSF 組合的好處,而且還可以高效地重復(fù)使用不同的 JSP 頁面,因?yàn)樗鼈儗⒂煽梢愿鶕?jù)需要添加或者刪除的組件部分或者 tiles 所構(gòu)成。

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

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

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

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

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

    在清單 6 和 7 后是每一個可能的挑戰(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)提交
    這是您在試圖訪問航班搜索表單時馬上會看到的第一個問題。小心查看堆棧跟蹤。您會看到問題出在類 com.sun.faces.lifecycle.ViewHandlerImpl 上。這是一個實(shí)現(xiàn)了 ViewHandler 接口的 JSF-RI 類。

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

    要解決這個問題,必須創(chuàng)建一個自定義的 ViewHandler 實(shí)現(xiàn),它將檢查響應(yīng)的狀態(tài)以確定它是否提交過。如果響應(yīng)沒有提交過,那么請求就轉(zhuǎn)發(fā)給下一頁,否則,就加入請求并顯示相應(yīng)的 JSP。我們將創(chuàng)建一個名為 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)呢?要回答這個問題,就必須理解 FacesServlet 的工作過程。

    在 Faces 初始化過程中, FacesServlet 會讓 LifecycleFactory 實(shí)現(xiàn)返回 Lifecycle 類的一個實(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)對象擁有在呈現(xiàn)響應(yīng)階段要使用的 ViewHandler 。您可以通過對 Lifecycle 實(shí)現(xiàn)調(diào)用 setViewHandler 方法讓自己的 ViewHandler 實(shí)現(xiàn)成為默認(rèn)的。

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

    清單 10. 注冊自定義 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時就會清楚。 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 取決于其初始化時的 Lifecycle 實(shí)現(xiàn),在 清單 10中展示的代碼應(yīng)該在 FacesServlet 初始化之前執(zhí)行。通過創(chuàng)建另一個 servlet 并在 FacesServlet 之前初始化它而做到這一點(diǎn)。

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

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

    這不是注冊一個帶有自定義 ViewHandlerLifecycle 惟一方法。事實(shí)上 FactoryFinder 實(shí)現(xiàn)了自己的發(fā)現(xiàn)算法以發(fā)現(xiàn) Factory 對象,包括 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)的問題后,單擊任何一個 Tiles 特定的鏈接或者輸入一個會呈現(xiàn) Faces 響應(yīng)的 URL。在這里,可以輸入顯示 FlightSearchForm 的 URL。

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

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

    清單 11. STFRequestProcessor.java

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

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

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

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

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

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

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

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

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

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

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

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

    清單 12 以粗體顯示對 createActionForm 方法的改變。我們沒有對 Struts-Faces 中的 FormComponent 作這些改變,而是通過繼承 FormComponent 并覆蓋 createActionForm() 方法創(chuàng)建了一個新的 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);
    ....
    ....
    

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

    FormRenderer 的改變
    必須對 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 改變時,標(biāo)記也必須改變。在這里,通過繼承 Struts-Faces 中的 FormTag 創(chuàng)建一個新的標(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)建了一個新組件 STFFormComponent 和一個新 renderer STFFormRenderer

    現(xiàn)在我們將在 faces-config.xml 文件中增加一個聲明,如清單 15 所示。 component-class 是組件的完全限定類名。 component-type 指的是在 STFFormTag ( 清單 12)中用于標(biāo)識組件的名字。以類似的方式發(fā)現(xiàn)和解釋 renderer。注意 faces-config.xml 文件是在 struts-faces.jar 文件中的。從 struts-faces.jar 文件中取出這個文件并將它放到 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 文件
    您不會在這個示例 Struts-Faces 應(yīng)用程序中看到 struts-faces.tld 文件,它打包到了 struts-faces.jar 文件中。打開并分析這個文件。它聲明了一個名為 org.apache.struts.faces.taglib.LifecycleListener 的類,這個類實(shí)現(xiàn)了 ServletContextListener 并初始化 FacesRequestProcessor

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

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

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

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

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

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

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

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

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

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

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

    • 新的 ViewHandler ,用于檢查提交的響應(yīng)。
    • 新的 ServletContextListener ,用于創(chuàng)建新的 Lifecycle 實(shí)現(xiàn)并注冊這個定制的 ViewHandler。
    • 新的 RequestProcessor ,用于處理 Tiles 請求。
    • 修改過的 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)用程序的一個強(qiáng)大而靈活的機(jī)制中的一個令人信服的路線圖。

    參考資料

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

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

    • Ant 用于對例子進(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,這是一個使用了 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月)是一個教程,主要關(guān)注在使用 WebShpere Studio Application Developer 作為開發(fā)環(huán)境時結(jié)合 Struts 使用 Tiles 模型框架。

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

    • A JSTL primer”( developerWorks,2003年2-5月),這是一個分為四部分的系列,提供了有關(guān) JSTL 的所有內(nèi)容,包括如何使用 JSTL 標(biāo)記以避免在 JSP 頁面中使用腳本元素、如何通過刪除表示層中的代碼簡化軟件維護(hù)、以及 JSTL 的簡化的表達(dá)式語言,它使得無需使用全功能的編程語言就可以為 JSTL action 指定動態(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)是另一個很好的學(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 閔毓 閱讀(710) 評論(0)  編輯  收藏 所屬分類: Java開發(fā)Struts in action
    主站蜘蛛池模板: 日韩免费人妻AV无码专区蜜桃| 69xx免费观看视频| 免费观看AV片在线播放| 日韩免费一区二区三区| 国产亚洲精品a在线观看app| 色偷偷亚洲女人天堂观看欧| eeuss草民免费| 大学生一级毛片免费看| 亚洲自偷自偷图片| 亚洲国产熟亚洲女视频| 在线观看免费黄色网址| 免费高清小黄站在线观看| 亚洲国产成人高清在线观看 | 中文字幕在线成人免费看| 一二三四免费观看在线视频中文版| 亚洲一区日韩高清中文字幕亚洲| 亚洲专区一路线二| 中国一级特黄高清免费的大片中国一级黄色片 | 97国产免费全部免费观看| 国产亚洲自拍一区| 亚洲一本一道一区二区三区| 国产精品网站在线观看免费传媒| 国产v片免费播放| 亚洲一区二区久久| 国产一区二区三区免费| 国产成人免费网站在线观看| 亚洲欧洲尹人香蕉综合| 中国国语毛片免费观看视频| 国产一区视频在线免费观看 | 香蕉视频在线免费看| 日韩一区二区三区免费体验| 亚洲精品高清国产麻豆专区| 久久国产乱子伦精品免费午夜| 免费看香港一级毛片| 亚洲欧洲国产成人精品| 一个人免费视频在线观看www| 四虎影视永久免费观看网址| 亚洲伊人久久大香线蕉| 免费人妻无码不卡中文字幕系| 国产亚洲精品福利在线无卡一| 在线观看亚洲免费|