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

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

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

    2006年11月7日

    使用 JSF 架構進行設計

    探索 JavaServer Faces 框架中使用的設計模式

    ?
    ??
    ?

    未顯示需要 JavaScript 的文檔選項


    拓展 Tomcat 應用

    下載 IBM 開源 J2EE 應用服務器 WAS CE 新版本 V1.1


    級別: 中級

    Anand Prakash Joshi (ananjosh@in.ibm.com), 軟件工程師, IBM

    2006 年 1 月 04 日

    本文中,作者 Anand Joshi 使用 JSF 框架中的設計模式闡釋了 JavaServer Faces (JSF) 體系結構。他討論了 JSF 體系結構中使用的 GoF 設計模式,以及這些模式在 JSF 框架中的作用。任何對設計模式和 JSF 體系結構有一定了解的人都能從 Anand 詳細的介紹中有所收獲。*讀者應該對 GoF 設計模式和 JSF 技術有很好的了解。

    設計模式可以幫助用戶在更高層次上抽象細節,更好地理解體系結構。如果比較熟悉 GoF 設計模式和 JavaServer Faces (JSF) 框架,本文可以幫助您洞察 JSF 框架中使用的設計模式,深入理解其工作原理。

    本文探討了 JSF 框架中使用的設計模式。詳細討論的設計模式包括 Singleton、Model-View-Controller、Factory Method、State、Composite、Decorator、Strategy、Template Method 和 Observer 模式。

    設計模式和 JavaServer Faces (JSF) 技術

    首先簡要地介紹一下模式和 JSF 框架。

    • 模式。設計模式是對問題和解決方案進行抽象的普遍適用的方法。因為模式是所有開發人員和架構師公認的,所以模式可以節約時間和資源。用外行話來說,模式就是關于某個人所共知的問題的經過驗證的解決方案。模式可以重用,重用使得解決方案更健壯。
    • Java Server Faces。 JSF 體系結構是一種 Web 應用程序框架。它是 Java Community Process (JCP) 推動的,有望成為 Web 應用程序開發的標準框架。目前用于開發 Web 應用程序的框架有 50 多個,這說明迫切需要實現框架的標準化,這正是 JSF 框架的目標!

    ?

    ?
    ?


    深入剖析 JSF 模式

    現在我們來討論 JSF 體系結構中的各種設計模式。本文將詳細討論 Singleton、Model-View-Controller、Factory Method、State、Composite、Decorator、Strategy、Template Method 和 Observer 設計模式。我將分析每種模式的用途及其在 JSF 框架中的作用。



    ?
    ?


    Singleton 模式

    Singleton 模式的目的是保證類只有一個實例被加載,該實例提供一個全局訪問點。當啟動具有 JSF 支持的 Web 應用程序時,Web 容器初始化一個 FacesServlet 實例。在這個階段,FacesServlet 對每個 Web 應用程序實例化 Application 和 LifeCycle 實例一次。這些實例就采用眾所周知的 Singleton 模式,通常只需要該類型的一個實例。

    使用 JSF 的 Web 應用程序只需要 Application 和 LifeCycle 類的一個實例。LifeCycle 管理多個 JSF 請求的整個生命期。因為其狀態和行為在所有請求之間共享,這些對象采用 Singleton 模式合情合理。LifeCycle 維護的 PhaseListeners 也是 Singleton 模式的。PhaseListeners 由所有 JSF 請求共享。在 JSF 框架中可以廣泛使用 Singleton 模式,以減少內存占用和提供對象的全局訪問。NavigationHandler(用于確定請求的邏輯結果)和 ViewHandler(用于創建視圖)也是使用 Singleton 模式的例子。




    ?
    ?


    Model-View-Controller (MVC)

    MVC 模式的目的是從數據表示(View)中將數據(即 Model)分離出來。如果應用程序有多種表示,可以僅替換視圖層而重用控制器和模型代碼。類似的,如果需要改變模型,可以在很大程度上不改變視圖層。控制器處理用戶動作,用戶動作可能造成模型改變和視圖更新。當用戶請求一個 JSF 頁面時,請求發送到 FacesServlet。FacesServlet 是 JSF 使用的前端控制器 servlet。和其他很多 Web 應用程序框架一樣,JSF 使用 MVS 模式消除視圖和模型之間的耦合。為了集中處理用戶請求,控制器 servlet 改變模型并將用戶導航到視圖。

    FacesServlet 是 JSF 框架中所有用戶請求都要經過的控制器元素。FacesServlet 分析用戶請求,使用托管 bean 對模型調用各種動作。后臺(backing)或托管(managed)bean 就是該模型的例子。JSF 用戶界面(UI)組件是視圖層的例子。MVC 模式把任務分解給具有不同技能的開發人員,使這些任務能夠同時進行,這樣 GUI 設計人員就可以使用豐富的 UI 組件創建 JSF 頁面,同時后端開發人員可以創建托管 bean 來編寫專門的業務邏輯代碼。




    ?
    ?


    Factory Method 模式

    Factory Method 模式的目的是定義一個用于創建對象的接口,但是把對象實例化推遲到子類中。在 JSF 體系結構中,Factory Method 模式被用于創建對象。LifeCycleFactory 是一個創建和返回 LifeCycle 實例的工廠對象。LifeCycleFactory 的 getLifeCycle (String LifeCycleId) 方法采用 Factory Method 模式,根據 LifeCycleId 創建(如果需要)并返回 LifeCycle 實例。自定義的 JSF 實現可以重新定義 getLifeCycle 抽象方法來創建自定義的 LifeCycle 實例。默認的 JSF 實現提供默認的 LifeCycle 實例。此外,對于每個 JSF 請求,FacesServlet 都從 FacesContextFactory 得到 FacesContext。FacesContextFactory 是一個抽象類,公開了 getFacesContext API,JSF 實現提供了 FacesContextFactory 和 getFacesContext API 的具體實現。這是另外一個使用 Factory Method 模式的例子,具體的 FacesContextFactory 實現創建 FacesContext 對象。




    ?
    ?


    State 模式

    State 模式的目的是在表示狀態的不同類之間分配與狀態有關的邏輯。FacesServlet 對 LifCycle 實例調用 execute 和 render 方法。LifeCycle 協調不同的 Phrase 以便執行 JSF 請求。在這里 JSF 實現就遵循了 State 模式。如果沒有使用這種模式,LifeCycle 實現就會被大量的條件(即 “if” 語句)攪得一塌糊涂。JSF 實現為每個狀態(或階段)創建單獨的類并調用 step。phase 是一個抽象類,定了每個 step 的公共接口。在 JSF 框架中定義了六個 phrase(即 step):RestoreViewPhase、ApplyRequestValues、ProcessValidationsPhase、UpdateModelValuesPhase、InvokeApplicationPhase 和 RenderResponsePhase。

    在 State 模式中,LifeCycle 把 FacesContext 對象傳遞給 phase。每個階段或狀態改變傳遞給它的上下文信息,然后設置 FacesContext 本身中的標志表明下一個可能的步驟。JSF 實現在每個步驟中改變其行為。每個階段都可以作為下一個階段的起因。FacesContext 有兩種標志 renderResponse 和 responseComplete 可以改變執行的順序。每個步驟執行完成后,LifeCycle 檢查上一階段是否設置了這些標志。如果設置了 responseComplete,LifeCycle 則完全放棄請求的執行。如果經過某個階段后設置了 renderResponse 標志,JSF 就會跳過剩下的階段而直接進入 Render Response 階段。如果這兩個標志都沒有設置,LifeCycle 就會按順序繼續執行下一步。




    ?
    ?


    Composite 模式

    Composite 模式讓客戶代碼能夠統一處理復合對象和基本對象。復合對象是基本對象的容器。在第一階段(Restore View 階段)和最后一個階段(Render Response 階段),使用 JSF UI 組件構造 UI View。UIComponentBase 就是 Composite 模式中 Component 抽象類的一個例子。UIViewRoot 是 Composite 類,而 UIOutput(比方說)就是葉子(或者基本類)。UIComponentBase 類定義了葉子和復合對象的公共方法,如編碼/解碼值和子節點管理函數。子節點管理函數,如 getChildren,對于葉子節點返回空列表,對于復合節點則返回其子節點。




    ?
    ?


    Decorator 模式

    Decorator 模式的目的是不通過子類化動態擴展對象的行為。JSF 框架有很多擴展點(即可插入機制)。JSF 實現可使用 Decorator 模式替換默認的 PropertyResolver、VariableResolver、ActionListener、NavigationHandler、ViewHandler 或 StateManager。通常自定義實現接受通過構造函數傳遞給它的默認實現的引用。自定義實現僅僅改寫功能的一個子集,而將其他功能委托給默認實現。如果希望實現自定義的 ViewHandler,改寫默認 ViewHandler 實現的 calculateLocale 方法,可以像 清單 1 那樣編寫 CustomViewHandler 類:

    清單 1. CustomViewHandler 片段

    public class CustomViewHandler extends ViewHandler {
     public CustomViewHandler(ViewHandler handler) {
    		 super();
    		 oldViewHandler = handler;
     }
    private ViewHandler oldViewHandler  = null;
    public void renderView (facesContext context, UIViewRoot view) {
                //delegate method to oldViewHandler
    		 oldViewHandler.renderView(context, view);
    }
    //custom implementation of calculateLocale
    public Locale calculateLocale(FacesContext context) {
    }
    }
    




    ?
    ?


    Strategy 模式

    Strategy 模式的目的是封裝不同的概念。JSF 框架采用 Strategy 模式使用委托實現模型呈現 UI 組件。JSF 技術支持兩種呈現模型。在直接實現模型中,UI 組件對收到的請求中的數據進行解碼,然后編碼這些數據進行顯示。在委托實現模型中,解碼和編碼操作委托給和組建關聯的專門呈現器。后一種模型利用了 Strategy 設計模式,比直接實現更靈活。在 Strategy 模式中,將不同的算法封裝在單獨的對象中,從而可以動態地改變算法。JSF 實現可以用已有的 renderkit 實例注冊另外的呈現器,當應用程序啟動的時候,JSF 實現讀取配置文件將這些呈現器和 UI 組件聯系在一起。




    ?
    ?


    Template Method 模式

    Template Method 模式的目的是將變化的步驟推遲到子類中,而在父類中定義那些固定的算法步驟。JSF 框架通過 PhraseListeners 展現了 Template Method 模式提供的功能。采用 Template Method(或者 “hook”)使得 Web 作者可以為不同階段之間的可選步驟提供實現,而主要階段仍然和 JSF 框架的定義一致。JSF 框架提供了 PhaseListeners,概念上類似于 Template Method 模式中的可變步驟。JSF 框架有六個預定義的階段,在每個階段之間,Web 作者可以實現 PhaseListeners 來提供類似于 Template Method hook 的 hook。事實上,這種結構比 Template Method 模式更具有擴展性。可以通過注冊 PhraseId 為 ANY_PHRASE 的 PhaseListener 在每個階段后提供 hook。如果 PhaseId 是 ANY_PHASE,JSF 實現就會在每個階段之前和之后調用該 PhaseListener。JSF 框架中的實現略有不同,因為可以根本沒有 PhaseListener,但是在 Template Method 模式中,子類通常重新定義父類中抽象的可變步驟。




    ?
    ?


    Observer 模式

    Observer 模式的目的是當目標對象的狀態改變時自動通知所有依賴的對象(即觀察器)。JSF 在 UI 組件中實現了 Observer 模式。JSF 有兩類內建事件:ActionEvent 和 ValueChangedEvent。ActionEvent 用于確定用戶界面組件(如按鈕)的激活。當用戶單擊按鈕時,JSF 實現通知添加到該按鈕上的一個或多個動作監聽程序。于是該按鈕被激活,或者說按鈕(主體)的狀態改變了。添加到按鈕上的所有監聽程序(即觀察器)都收到通知該主體狀態已經改變。類似的,當輸入 UI 組件中的值改變時,JSF 實現通知 ValueChangeListener。




    ?
    ?


    結束語

    JSF 框架利用了 Singleton、Model-View-Controller、Factory Method、State、Composite、Decorator、Strategy、Template Method 和 Observer 設計模式。因為它的體系結構建立在已經驗證的設計模式的基礎上,這是一個健壯的框架,模式在 JSF 框架中得到了很好的利用。



    參考資料

    學習

    獲得產品和技術

    討論


    關于作者

    Anand Joshi 的照片

    Anand 是一位 Sun 認證的企業架構師,幾年來一直研究 Web 技術。他對 WebSphere 管理控制臺應用程序的設計和開發做了多方面的貢獻。Anand 曾經在 IBM 美國工作過幾年,目前在 IBM 印度工作。


    posted @ 2006-11-08 09:51 GoodtigerZhao 閱讀(315) | 評論 (0)編輯 收藏
     

    JSF是一種新的用于構架j2ee應用用戶界面的技術,它尤其適合于基于MVC架構的應用中。雖已有很多文章介紹過了JSF,然而它們大多從理論高度來介紹JSF而不是面向于實際應用。目前對于實際應用,JSF仍有很多問題沒有解決,例如:如何使JSF適應于MVC整體構架中?如何將JSF與其他Java 框架整合起來?是否應該將業務邏輯放置在JSF的backing beans中?如何處理JSF中的安全機制?更為重要的是如何利用JSF構架現實世界的Web應用?

    本文將涉及到上面的這些問題,它將演示如何將JSF、Spring和Hibernate整合在一起,構架出一個名為JCatalog的在線產品價目系統
    。利用該Demo,本文涵蓋了Web應用開發的每一個階段,包括需求收集、分析,技術選擇,系統架構和實現。本文討論了在JCatalog中涉及到的各種技術的優點和缺點并展示了一些關鍵部分的設計方法。

    本文的對象是從事基于J2ee的Web應用架構人員和開發人員,它并不是對JSF、SpringFramework和Hibernate的簡單介紹,如果對這些領域不甚了解,請參看相關資源。

    該范例的功能需求
    JCatalog是一個現實世界的Web應用,我首先描述JCatalog的需求,在通篇的技術決策和架構設計時都將涉及到本部分。

    在設計Web應用的第一階段是收集系統的功能需求,范例應用是一個典型的電子商務應用系統,用戶可以瀏覽產品的catalog并查看產品的詳細情況,而管理員可以管理產品的catalog。通過增加一些其他功能,如inventory管理和訂單處理等,該應用可成為一個成熟的電子商務系統。

    Use cases
    Use-case分析被用來展示范例應用的功能需求,圖1就是該應用的use-case圖。
    ????


    use-case圖用于表示系統中的actors以及可能進行的operations,在該應用中將有七個use-case,用戶能夠瀏覽產品 catalog和查看產品的詳細情況,一旦用戶登錄到系統中,她將成為管理員,從而可以創建新的產品,編輯已存在的產品或者刪除老的產品等。

    Business rules
    JCatalog必須符合以下business rules:
    • 每個產品必須具有唯一的ID
    • 每個產品必須屬于至少一個category
    • 產品ID一旦創立不得修改
    Assumptions
    我們在系統的設計和實現中做以下假定:
    • 英語講是缺省語言,且不需事先國際化
    • 在Catalog不講不會超過500個產品
    • catalog將不會被頻繁的修改
    Page flow
    圖2顯示了所有的JCatalog的pages以及它們之間的transitions關系:
    ????


    該應用中存在兩組pages:公開的internet和用于管理的intranet,其中intranet只能被那些成功登錄到系統的用戶訪問。 ProductSummary不作為一個單獨的page展示給用戶,它顯示在Catalog page中的frame中。ProductList只對管理員可視,它包含用于創建、編輯和刪除產品的鏈接。

    圖3是一個Catalog頁面的示意圖,理想狀況下,在需求文檔中應該包含每一頁的詳細示意圖。
    ????????


    構架設計
    Web應用開發的下一個階段是構架設計,它包括將應用劃分為多個功能組件并將這些組件分割組合成層,高層的構架設計應該中立于所選用的特定技術。

    多層架構
    多層架構是將整個系統清晰的分為多個功能單元:client、presentation、business-logic、integration和 EIS,這將確保職責得到清晰的劃分,使得系統更易于維護和擴展。具有三層或等多層的系統被證明比C/S模型具有更好的伸縮性和靈活性。

    client層是使用和表示數據模型的地方,對于一個Web應用,client層通常是瀏覽器,基于瀏覽器的瘦客戶端不包含任何表示邏輯,它依賴于
    presentation 層。

    presentation層將business-logic層的服務展示給用戶,它應知道如何處理用戶的請求,如何同business-logic層交互,并且知道如何選擇下一個視圖顯示給用戶。

    business-logic層包含應用的business objects和business services。它接受來在于presentation層的請求、基于請求處理業務邏輯。業務邏輯層組件將受益于系統級的服務,如安全管理、事務管理和資源管理等。

    integration層是介于
    business-logic 層和EIS層之間的橋梁,它封裝了與EIS層交互的邏輯。有時,將integration層和business-logic層合稱為中間層。

    應用的數據被保存在EIS層中,它包括關系數據庫、面向對象數據庫和以及遺留系統等。

    JCatalog的構架設計
    圖4顯示了JCatalog的構架設計以及如何應用于多層構架系統中。
    ?


    該應用采用了多層非分布式的構架,圖4展示了系統的分層以及每一層中選擇的技術,它同時又是該范例的部署圖,它的presentation、 business-logic和integration層將存在于同一個web容器中。定義良好的接口將孤立每一層的職責,這一架構使得應用更為簡單和更好的伸縮性。

    對于presentation層,經驗表明,最好的方法是選擇已存在的并已得到證明了的Web應用框架,而不是自己去設計和開發新的框架。我們擁有多個可選擇的框架,如Struts,WebWork和JSF等,在JCatalog中,我們選擇采用JSF。

    EJB和POJO都可以用來創建業務邏輯層,如果應用是分布式的,采用具有remote接口的EJB是一個好的選擇;由于JCatalog是一個典型的不需要遠程訪問的Web應用,因此選用POJO,并充分利用Spring Framework的幫助,將是實現業務邏輯層的更好選擇。

    integration層利用關系型數據庫事先數據的持續化,存在多種方法可用來實現:
    • JDBC:這是最為靈活的方法,然而,低級的JDBC難以使用,而且質量差的JDBC代碼很難運轉良好
    • Entity beans:CMP的Entity bean是一種分離數據訪問代碼和處理ORM的昂貴的方法,它是以應用服務器為中心的方法,即entity bean不是將應用與某種數據庫類型而是EJB容器約束在一起。
    • O/R mapping framework:一個ORM框架采用以對象為中心的方法實現數據持續化,一個以對象為中心的應用易于開發并具有高度的可移植性。在該領域中存在幾個框架可用—JDO、Hibernate、TopLink以及CocoBase等。在我們的范例中將選用Hibernate。
    現在,我們將討論每一層中的設計問題,由于JSF是一個相對較新的技術,因此將著重于它的使用:

    presentation 層和JSF
    表示層的功能是收集用戶的輸入、展示數據、控制頁面導航并將用戶的輸入傳遞給業務邏輯層,表示層同時需要驗證用戶的輸入以及維護應用的session狀態。在下面幾部分中,我將討論表示層設計時的考慮和模式,并說明選擇JSF作為JCatalog表示層的原因。

    MVC
    MVC是Java-Blueprints推薦的架構設計模式,MVC將幾個方面分離開來,從而減少代碼的重復,它以控制為中心并使得應用更具擴展性。MVC同時可幫助具有不同技能的用戶更關注于自己的技能,通過定義良好的接口進行相互合作。MVC是表示層的架構設計模式。

    JSF
    JSF是Web應用的服務器端用戶組件框架,它包含以下API:表示UI組件、管理它們的狀態、處理事件、服務器端驗證、數據轉換、定義頁面導航、支持國際化,并為這些特性提供擴展能力。它同時包括兩個JSP的tag庫以在JSP頁面中表示UI組件,以及將組件wire為服務器端對象。

    JSF和MVC
    JSF非常適合于基于MVC的表示層架構,它在行為和表示之間提供了清晰的分離,它使得你可以采用熟悉的UI組件和web層概念而無需受限于某種特殊的腳本技術或標記語言。

    JSF backing beans是JSF的Model層,此外,它同樣包含actions,action是controller層的擴展,用于將用戶的請求委派給業務邏輯層。這里請注意,從整體的應用構架看,業務邏輯層也被稱為model層。包含JSF標簽的JSP頁面是表示層,Faces Servlet提供了controller的功能。

    為什么選用JSF?

    JSF不僅僅是另外一個Web框架,下面這些特性是JSF區別于其他Web框架之所在:
    • 類Swing的面向對象的Web應用開發:服務器端有狀態的UI組件模型,配合event listeners和handlers,促進了面向對象的Web應用開發。
    • backing-bean管理: backing bean是與頁面中使用的UI組件相關聯的javabean組件,backing-bean管理將UI組件對象的定義同執行應用相關處理和擁有數據的對象分離開來。JSF在合適的范圍內保存和管理這些backing-bean實例。
    • 可擴展的UI模型:JSF的UI模型是可配置的、可重用的,用以構建JSF應用的用戶界面。你可以通過擴展標準的UI組件來開發出更為復雜的組件,例如菜單條、樹組件等。
    • 靈活的rendering模型:renderer分離了UI組件的功能和顯示,多個renderers可創建和用來為同一客戶端或不同的客戶端定義不同的顯示。
    • 可擴展的轉換和驗證模型:基于標準的converter和validator,你可以開發出自己的可提供更好的模型保護的converter和validator。
    盡管如此,JSF目前尚未成熟,隨同JSF發布的 components、converters和validators都是最基礎的,而且per-component驗證模型不能處理components 和validators間的many-to-many驗證。此外,JSF標簽不能與JSTL間無縫的整合在一起。

    在下面的章節中,我將討論幾個在JCatalog實現中的關鍵部分和設計決策。我首先解釋managed bean的定義和使用以及JSF中的backing bean,然后,我將說明如何處理安全、分頁、caching、file upload、驗證以及錯誤信息定制。

    Managed bean,backing bean,view object 和domain object model
    JSF中引入了兩個新的名詞:managed bean和backing bean。JSF提供了一個強大的managed-bean工具,由JSF來管理的JavaBean對象稱為managed-bean,一個 managed bean表述了一個bean如何被創建和管理,它不包含該bean的任何功能性描述。

    backing bean定義了與頁面中使用的UI組件相關聯的屬性和處理邏輯。每一個backing-bean屬性邦定于一個組件實例或某實例的value。一個 backing-bean同時定義了一組執行組件功能的方法,例如驗證組件的數據、處理組件觸發的事件、實施與組件相關的導航等。

    一個典型的JSF應用將其中的每個頁面和一個backing-bean結合起來,然而在現實應用中,強制的執行這種one-on-one的關系不是一種理想的解決方案,它可能會導致代碼重復等問題。在現實的應用中,多個頁面可以共享一個backing-bean,例如在JCatalog中, CreateProduct和EditProduct將共享同一個ProductBean定義。

    model對象特定于表示層中的一個view對象,它包含必須顯示在view層的數據以及驗證用戶輸入、處理事件和與業務邏輯層交互的處理邏輯等。在基于 JSF的應用中backing bean就是view對象,在本文中backing bean和view對象是可互換的名詞。

    對比于struts中的ActionForm和Action,利用JSF中的backing-bean進行開發將能更好的遵循面向對象方法,一個 backing-bean不僅包含view數據,而且還包含與這些數據相關的行為,而在struts中,Action和ActionForm分別包含數據和邏輯。

    我們都應該聽說過domain object model,那么,domain object model和view對象之間有什么區別呢?在一個簡單的Web應用中,一個domain object model能夠橫穿所有層中,而在復雜的應用中,需要用到一個單獨的view對象模型。domain object model應該屬于業務邏輯層,它包含業務數據和與特定業務對象相關的業務邏輯;一個view對象包含presentation-specific的數據和邏輯。將view對象從domain object model中分離出來的缺點是在這兩個對象模型之間必將出現數據映射。在JCatalog中,ProductBeanBuilder和 UserBeanBuilder利用reflection-based Commons BeanUtils來實現數據映射。

    安全
    目前,JSF沒有內建的安全特性,而對于范例應用來說安全需求是非常基礎的:用戶登錄到administration intranet中僅需用戶名和密碼認證,而無需考慮授權。
    針對于JSF的認證,已有幾種方法提出:
    • 利用一個backing bean:這一個方法非常簡單,然而它卻將backing bean與特殊的繼承關系結合起來了
    • 利用JSF的ViewHandler decorator:這一方法中,安全邏輯緊密地與一特定Web層技術聯系在了一起
    • 利用servlet filter:一個JSF應用與其他的Web應用沒有什么兩樣,filter仍是處理認證檢查的最好地方,這種方法中,認證邏輯與Web應用分離開來
    在我們的范例程序中,SecurityFilter類被用來處理用戶的認證,目前,受保護的資源只包含三個頁面,出于簡單的考慮,將它們的位置被硬編碼到Filter類中。

    分頁
    該應用中的Catalog頁面需要分頁,表示層可用來處理分頁,即它取出所有的數據并保存在這一層;分頁同樣可在business-logic層、 integration層、甚至EIS層中實現。由于在JCatalog中假設不超過500個產品,因此所有的產品信息能存放在一個user session中,我們將分頁邏輯放在了ProductListBean中,與分頁相關的參數將通過JSF managed-bean工具配置。

    Caching
    Caching是提高Web應用性能的最重要技術之一,在應用構建中的很多層中都可以實現caching。JSF managed-bean工具可以使在表示層實現caching非常容易。通過改變一個managed bean的范圍,這個managed bean中包含的數據可以在不同的范圍內緩存。

    范例應用中采用了兩級caching,第一級caching存在于業務邏輯層,CachedCatalogServiceImpl類維護了一個所有產品和目錄的讀寫cache,Spring將該類作為一個singleton service bean來管理,所以,一級cache是一個應用范圍的讀寫cache。

    為了簡化分頁邏輯并進而提高應用的速度,產品同樣在session范圍內緩存到表示層,每一個用戶維護著他自己的ProductListBean,這一方法的缺點是內存的消耗和數據的失效問題,在一個用戶session中,如果管理員更改了catalog,用戶可到的將是失效的數據,然而,由于我們假設應用的數據不會經常的改變,所以這些缺點將能夠忍受。

    File upload
    目前的JSF Sun參考實現中不支持file upload。Struts雖已具有非常不錯的file upload能力,然而要想使用這一特性需要Struts-Faces整合庫。在JCatalog中,一個圖像與一個產品相關聯,在一個用戶創建了新的產品后,她必須將相應的圖片上傳,圖片將保存在應用服務器的文件系統里,產品的ID就是圖像名稱。

    范例應用中采用、Servlet和Jakarta Common的file-upload API來實現簡單的文件上傳功能,該方法包含兩個參數:圖像路徑和圖像上傳結果頁面。它們將通過ApplicationBean來配置,詳細內容請參看 FileUploadServlet類。

    Validation
    JSF中發布的標準validator是非常基礎的,無法滿足現實的需要,但很容易開發出自己的JSF validator,在范例中,我開發了SelectedItemsRange validator,它用來驗證UISelectMany組件中選擇的數量:

    ?
    ? ??
    ????
    ?


    詳細情況請參看范例。

    定制錯誤信息
    在JSF中,你可以為converters和validators創建resource bundle和定制錯誤信息,一個resource bundle可在faces-config.xml中創建:

    ? catalog.view.bundle.Messages

    并將錯誤信息的key-value對加到Message.properties文件中:

    ? javax.faces.component.UIInput.CONVERSION=Input data is not in the correct type.
    ? javax.faces.component.UIInput.REQUIRED=Required value is missing.

    業務邏輯層和Spring Framework
    業務對象和業務服務存在于業務邏輯層中,一個業務對象不僅包含數據,而且包含相應的邏輯,在范例應用中包含三個業務對象:Product、Category和User。

    業務服務與業務對象交互并提供更高級的業務邏輯,需要首先定義一個正式的業務接口,它是直接與終端用戶交互的服務接口。在JCatalog中,通過在 Spring Framework幫助下的POJO實現業務邏輯層,其中共有兩個業務服務:CatalogService包含Catalog管理相關的業務邏輯, UserService中包含User管理邏輯。

    Spring是基于IoC概念的框架,在范例應用中用到的Spring特性包括:
    • Bean management with application contexts:Spring可以有效地組織我們的中間層對象,它能夠消除singleton的proliferation,并易于實現良好的面向對象編程方法,即“編程到接口”。
    • Declarative Transaction management: Spring利用AOP實現事務管理,而無需借助于EJB容器,利用這種方法,事務管理可以用于任何POJO中。Spring的事務管理不局限于JTA,而是可以采用不同的事務策略,在范例應用中,我們將使用declarative transaction management with Hibernate transaction。
    • Data-access exception hierarchy:Spring提供了非常好的異常來代替SQLException,為利用Spring的異常,必須在Spring的配置文件中定義以下異常轉換:
    ???????
    ? ? ? ? ?
    ?????????????
    ?????????

    ???????


    ??? ??? 在范例應用中,如果一個具有重復ID的新產品被插入,將會拋出DataIntegrityViolationException,這一異常將被
    ??????? catch并rethrown一個DuplicateProductIdException。這樣,該異常就可以與其它的異常區別處理。
    • Hibernate integration:Spring與Hibernate這樣的ORM框架整合的非常好,Spring提供了對Hibernate session的高效和安全的處理,它可通過application context配置Hibernate的SessionFactories和JDBC數據源,并使得應用易于測試。

    Integration層和Hibernate
    Hibernate是一個開源的ORM框架,它可以支持所有主流SQL數據庫系統,Hibernate的查詢語言為對象和關系架起了非常好的橋梁。Hibernate提供了強大的功能以實現:數據讀取和更新、事務管理、數據連接池、查詢和實體關系管理等。

    Data Access Ojbect(DAO)
    JCatalog中采用了Dao模式,該模式抽象和封裝了所有對數據源的訪問,該應用中包括兩個DAO接口:CatalogDao和UserDao,它們相應的實現HibernateCatalogDaoImpl和HibernateUserDAoImpl包含了Hibernate特定的邏輯來實現數據的管理和持久化。

    實現
    現在我們來看看如何將上面討論的這些東西包裝在一起以實現JCatalog,你可以從這個地址下載源碼:source code

    數據庫設計
    我們為該范例應用創建了包含4個表的數據庫,如圖5所示:


    類設計
    圖6顯示了JCatalog的類圖


    “編程到接口”的思想貫穿了整個設計實現中,在表示層,共用到四個backing bean:ProductBean、ProductListBean、UserBean和MessageBean;業務邏輯層包含兩個業務服務 (CatalogService和UserService)和三個業務對象(Product、Category和User);Integration層有兩個Dao接口和它們相應的Hibernate實現,Spring的application context用來管理絕大多數的業務邏輯層和integration層的對象;ServiceLocator將JSF和業務邏輯層整合在了一起。

    Wire everything up
    由于篇幅所限,我們僅舉例說明,范例中use case CreateProduct展示了如何裝配和構建應用,在詳細講述細節前,我們利用sequence圖(圖7)來說明所有層的end-tp-end整合。



    表示層
    表示層實現包括創建JSP頁面、定義頁導航、創建和配置backing bean以及將JSF與業務邏輯層整合。
    • JSP page:createProduct.jsp是用來創建新產品的頁面,它包含UI組件并將組件打包成ProductBean,ValidateItemsRange標簽用來驗證用戶選擇的種類數量,對每一個產品至少要有一個種類被選中。
    • 頁面導航:應用中的導航被定義在應用的配置文件faces-navigation.xml中,CreateProduct的導航準則如下:

    ?? *
    ??
    ????? createProduct
    ????? /createProduct.jsp
    ??


    ?? /createProduct.jsp
    ??
    ????? success
    ????? /uploadImage.jsp
    ??
    ??
    ????? retry
    ????? /createProduct.jsp
    ??
    ??
    ????? cancel
    ????? /productList.jsp
    ??

    • Backing bean:ProductBean不僅包含有將數據映射到頁面上的UI組件的屬性,還包括三個action:createAction、editAction和deleteAction,下面是createAction方法的代碼:
    public String createAction() {
    ?? try {
    ????? Product product = ProductBeanBuilder.createProduct(this);

    ????? //Save the product.
    ????? this.serviceLocator.getCatalogService().saveProduct(product);

    ????? //Store the current product id inside the session bean.
    ????? //For the use of image uploader.
    ????? FacesUtils.getSessionBean().setCurrentProductId(this.id);

    ????? //Remove the productList inside the cache.
    ????? this.logger.debug("remove ProductListBean from cache");
    ????? FacesUtils.resetManagedBean(BeanNames.PRODUCT_LIST_BEAN);
    ?? } catch (DuplicateProductIdException de) {
    ????? String msg = "Product id already exists";
    ????? this.logger.info(msg);
    ????? FacesUtils.addErrorMessage(msg);

    ????? return NavigationResults.RETRY;
    ?? } catch (Exception e) {
    ????? String msg = "Could not save product";
    ????? this.logger.error(msg, e);
    ????? FacesUtils.addErrorMessage(msg + ": Internal Error");

    ????? return NavigationResults.FAILURE;
    ?? }
    ?? String msg = "Product with id of " + this.id + " was created successfully.";
    ?? this.logger.debug(msg);
    ?? FacesUtils.addInfoMessage(msg);

    ?? return NavigationResults.SUCCESS;
    }
    • Managed-bean聲明:ProductBean必須在JSF配置文件faces-managed-bean.xml中配置:

    ??
    ????? Backing bean that contains product information.
    ??
    ?? productBean
    ?? catalog.view.bean.ProductBean
    ?? request???
    ??
    ????? id
    ????? #{param.productId}
    ??
    ??
    ????? serviceLocator
    ????? #{serviceLocatorBean}
    ??

    • ?表示層和業務邏輯層之間的整合: ServiceLocator抽象了查找服務的邏輯,在范例應用中,ServiceLocator被定義為一個接口,該接口實現為一個JSF的 managed bean,即ServiceLocatorBean,它將在Spring的application context中尋找服務:
    ServletContext context = FacesUtils.getServletContext();
    this.appContext = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
    this.catalogService = (CatalogService)this.lookupService(CATALOG_SERVICE_BEAN_NAME);
    this.userService = (UserService)this.lookupService(USER_SERVICE_BEAN_NAME);
    業務邏輯層
    • 業務對象:由于采用Hibernate提供持久化,因此Product和Category兩個業務對象需要為它們的所有field提供getter和setter。
    • 業務服務:CatalogService接口中定義了所有的與Catalog management相關的服務:
    public interface CatalogService {
    ?? public Product saveProduct(Product product) throws CatalogException;
    ?? public void updateProduct(Product product) throws CatalogException;
    ?? public void deleteProduct(Product product) throws CatalogException;
    ?? public Product getProduct(String productId) throws CatalogException;
    ?? public Category getCategory(String categoryId) throws CatalogException;
    ?? public List getAllProducts() throws CatalogException;
    ?? public List getAllCategories() throws CatalogException;
    }
    • Spring Configuration:這里是CatalogService的Spring配置:


    ??




    ??




    ??
    ??
    ??
    ?????
    ???????? PROPAGATION_REQUIRED,readOnly
    ?????? PROPAGATION_REQUIRED
    ?????? PROPAGATION_REQUIRED
    ?????? PROPAGATION_REQUIRED
    ?????
    ??

    • Spring和Hibernate的整合:下面是HibernateSessionFactory的配置:

    <!-- Hibernate SessionFactory Definition -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
    ?? <property name="mappingResources">
    ??????<list>
    ???????? <value>catalog/model/businessobject/Product.hbm.xml</value>
    ???????? <value>catalog/model/businessobject/Category.hbm.xml</value>
    ???????? <value>catalog/model/businessobject/User.hbm.xml</value>
    ??????</list>
    ?? </property>
    ?? <property name="hibernateProperties">
    ??????<props>
    ???????? <prop key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop>
    ?????? <prop key="hibernate.show_sql">true</prop>
    ?????? <prop key="hibernate.cglib.use_reflection_optimizer">true</prop>
    ?????? <prop key="hibernate.cache.provider_class">net.sf.hibernate.cache.HashtableCacheProvider</prop>
    ??????</props>
    ?? </property>
    ?? <property name="dataSource">
    ??????<ref bean="dataSource"/>
    ?? </property>
    </bean>

    CatalogDao uses HibernateTemplate to integrate between Hibernate and Spring. Here's the configuration for HibernateTemplate:

    <!-- Hibernate Template Defintion -->
    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate.HibernateTemplate">
    ?? <property name="sessionFactory"><ref bean="sessionFactory"/></property>
    ?? <property name="jdbcExceptionTranslator"><ref bean="jdbcExceptionTranslator"/></property>
    </bean>



    Integration層
    Hibernate通過xml配置文件來映射業務對象和關系數據庫,在JCatalog中,Product.hbm.xml表示了Product對象的映射,Category.hbm.xml則用來表示Category的映射,Product.hbm.xml如下:

    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC
    ??????"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
    ??????"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
    <hibernate-mapping package="catalog.model.businessobject">
    ?? <class name="Product" table="product">
    ??????<id name="id" column="ID" unsaved-value="null">
    ???????? <generator class="assigned"/>
    ??????</id>
    ??????<property name="name" column="NAME" unique="true" not-null="true"/>
    ??????<property name="price" column="PRICE"/>????
    ??????<property name="width" column="WIDTH"/>??????
    ??????<property name="height" column="height"/>??????
    ??????<property name="description" column="description"/> ??
    ??????<set name="categoryIds" table="product_category" cascade="all">
    ???????? <key column="PRODUCT_ID"/>
    ???????? <element column="CATEGORY_ID" type="string"/>
    ??????</set>
    ?? </class>
    </hibernate-mapping>

    CatalogDao is wired with HibernateTemplate by Spring:

    <!-- Catalog DAO Definition: Hibernate implementation -->
    <bean id="catalogDao" class="catalog.model.dao.hibernate.CatalogDaoHibernateImpl">
    ?? <property name="hibernateTemplate"><ref bean="hibernateTemplate"/></property>
    </bean>

    結論
    本文主要講述了如何將JSF與Spring、Hibernate整合在一起來構建實際的Web應用,這三種技術的組合提供了一個強大的Web應用開發框架。在Web應用的高層設計中應該采用多層構架體系,JSF非常適合MVC設計模式以實現表示層,Spring可用在業務邏輯層中管理業務對象,并提供事物管理和資源管理等,Spring與Hibernate結合的非常出色,Hibernate是強大的O/R映射框架,它可以在integration層中提供最好的服務。

    通過將整個Web應用分割成多層,并借助于“編程到接口”,應用程序的每一層所采用的技術都是可替換的,例如Struts可以用來替換JSF,JDO可替換Hibernate。各層之間的整合不是不值得研究,采用IoC和ServiceLocator設計模式可使得整合非常容易。JSF提供了其它Web框架欠缺的功能,然而,這并不意味著你馬上拋棄Struts而開始使用JSF,是否采用JSF取決于項目目前的狀況和功能需求,以及開發團隊的意見等。
    posted @ 2006-11-07 19:46 GoodtigerZhao 閱讀(285) | 評論 (0)編輯 收藏
     

    板橋里人 http://www.jdon.com 2005/09/05

      Struts和JSF/Tapestry都屬于表現層框架,這兩種分屬不同性質的框架,后者是一種事件驅動型的組件模型,而Struts只是單純的MVC模式框架,老外總是急吼吼說事件驅動型就比MVC模式框架好,何以見得,我們下面進行詳細分析比較一下到底是怎么回事?

      首先事件是指從客戶端頁面(瀏覽器)由用戶操作觸發的事件,Struts使用Action來接受瀏覽器表單提交的事件,這里使用了Command模式,每個繼承Action的子類都必須實現一個方法execute。

      在struts中,實際是一個表單Form對應一個Action類(或DispatchAction),換一句話說:在Struts中實際是一個表單只能對應一個事件,struts這種事件方式稱為application event,application event和component event相比是一種粗粒度的事件。

      struts重要的表單對象ActionForm是一種對象,它代表了一種應用,這個對象中至少包含幾個字段,這些字段是Jsp頁面表單中的input字段,因為一個表單對應一個事件,所以,當我們需要將事件粒度細化到表單中這些字段時,也就是說,一個字段對應一個事件時,單純使用Struts就不太可能,當然通過結合JavaScript也是可以轉彎實現的。

      而這種情況使用JSF就可以方便實現,

    <h:inputText id="userId" value="#{login.userId}">
      <f:valueChangeListener type="logindemo.UserLoginChanged" />
    </h:inputText>

      #{login.userId}表示從名為login的JavaBean的getUserId獲得的結果,這個功能使用struts也可以實現,name="login" property="userId"

      關鍵是第二行,這里表示如果userId的值改變并且確定提交后,將觸發調用類UserLoginChanged的processValueChanged(...)方法。

      JSF可以為組件提供兩種事件:Value Changed和 Action. 前者我們已經在上節見識過用處,后者就相當于struts中表單提交Action機制,它的JSF寫法如下:

    <h:commandButton id="login" commandName="login">
      <f:actionListener type=”logindemo.LoginActionListener” />
    </h:commandButton>

      從代碼可以看出,這兩種事件是通過Listerner這樣觀察者模式貼在具體組件字段上的,而Struts此類事件是原始的一種表單提交Submit觸發機制。如果說前者比較語言化(編程語言習慣做法類似Swing編程);后者是屬于WEB化,因為它是來自Html表單,如果你起步是從Perl/PHP開始,反而容易接受Struts這種風格。

    基本配置

      Struts和JSF都是一種框架,JSF必須需要兩種包JSF核心包、JSTL包(標簽庫),此外,JSF還將使用到Apache項目的一些commons包,這些Apache包只要部署在你的服務器中既可。

      JSF包下載地址:http://java.sun.com/j2ee/javaserverfaces/download.html選擇其中Reference Implementation。

      JSTL包下載在http://jakarta.apache.org/site/downloads/downloads_taglibs-standard.cgi

      所以,從JSF的驅動包組成看,其開源基因也占據很大的比重,JSF是一個SUN伙伴們工業標準和開源之間的一個混血兒。

      上述兩個地址下載的jar合并在一起就是JSF所需要的全部驅動包了。與Struts的驅動包一樣,這些驅動包必須位于Web項目的WEB-INF/lib,和Struts一樣的是也必須在web.xml中有如下配置:

    <web-app>
      <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
      </servlet>

      <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.faces</url-pattern>
      </servlet-mapping>
    </web-app>

      這里和Struts的web.xml配置何其相似,簡直一模一樣。

      正如Struts的struts-config.xml一樣,JSF也有類似的faces-config.xml配置文件:


    <faces-config>
      <navigation-rule>
        <from-view-id>/index.jsp</from-view-id>
        <navigation-case>
          <from-outcome>login</from-outcome>
          <to-view-id>/welcome.jsp</to-view-id>
        </navigation-case>
      </navigation-rule>

      <managed-bean>
        <managed-bean-name>user</managed-bean-name>
        <managed-bean-class>com.corejsf.UserBean</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
      </managed-bean>
    </faces-config>

    ?

      在Struts-config.xml中有ActionForm Action以及Jsp之間的流程關系,在faces-config.xml中,也有這樣的流程,我們具體解釋一下Navigation:

      在index.jsp中有一個事件:

    <h:commandButton label="Login" action="login" />

      action的值必須匹配form-outcome值,上述Navigation配置表示:如果在index.jsp中有一個login事件,那么事件觸發后下一個頁面將是welcome.jsp

      JSF有一個獨立的事件發生和頁面導航的流程安排,這個思路比struts要非常清晰。

      managed-bean類似Struts的ActionForm,正如可以在struts-config.xml中定義ActionForm的scope一樣,這里也定義了managed-bean的scope為session。

      但是如果你只以為JSF的managed-bean就這點功能就錯了,JSF融入了新的Ioc模式/依賴性注射等技術。

    Ioc模式

      對于Userbean這樣一個managed-bean,其代碼如下:

    public class UserBean {
      private String name;
      private String password;

      // PROPERTY: name
      public String getName() { return name; }
      public void setName(String newValue) { name = newValue; }

      // PROPERTY: password
      public String getPassword() { return password; }
      public void setPassword(String newValue) { password = newValue; }
    }

    <managed-bean>
      <managed-bean-name>user</managed-bean-name>
      <managed-bean-class>com.corejsf.UserBean</managed-bean-class>
      <managed-bean-scope>session</managed-bean-scope>

      <managed-property>
        <property-name>name</property-name>
        <value>me</value>
      </managed-property>

      <managed-property>
        <property-name>password</property-name>
        <value>secret</value>
      </managed-property>
    </managed-bean>

      faces-config.xml這段配置其實是將"me"賦值給name,將secret賦值給password,這是采取Ioc模式中的Setter注射方式

    Backing Beans

      對于一個web form,我們可以使用一個bean包含其涉及的所有組件,這個bean就稱為Backing Bean, Backing Bean的優點是:一個單個類可以封裝相關一系列功能的數據和邏輯。

      說白了,就是一個Javabean里包含其他Javabean,互相調用,屬于Facade模式或Adapter模式。


      對于一個Backing Beans來說,其中包含了幾個managed-bean,managed-bean一定是有scope的,那么這其中的幾個managed-beans如何配置它們的scope呢?

    <managed-bean>
      ...
      <managed-property>
        <property-name>visit</property-name>
        <value>#{sessionScope.visit}</value>
      </managed-property>

      這里配置了一個Backing Beans中有一個setVisit方法,將這個visit賦值為session中的visit,這樣以后在程序中我們只管訪問visit對象,從中獲取我們希望的數據(如用戶登陸注冊信息),而visit是保存在session還是application或request只需要配置既可。

    UI界面

      JSF和Struts一樣,除了JavaBeans類之外,還有頁面表現元素,都是是使用標簽完成的,Struts也提供了struts-faces.tld標簽庫向JSF過渡。

      使用Struts標簽庫編程復雜頁面時,一個最大問題是會大量使用logic標簽,這個logic如同if語句,一旦寫起來,搞的JSP頁面象俄羅斯方塊一樣,但是使用JSF標簽就簡潔優美:

    <jia:navigatorItem name="inbox" label="InBox"
      icon="/images/inbox.gif"
      action="inbox"
      disabled="#{!authenticationBean.inboxAuthorized}"/>

      如果authenticationBean中inboxAuthorized返回是假,那么這一行標簽就不用顯示,多干凈利索!

      先寫到這里,我會繼續對JSF深入比較下去,如果研究過Jdon框架的人,可能會發現,Jdon框架的jdonframework.xml中service配置和managed-bean一樣都使用了依賴注射,看來對Javabean的依賴注射已經迅速地成為一種新技術象征,如果你還不了解Ioc模式,趕緊補課。

    附Jsf核心教程一個JSF案例:login.rar

    相關討論:

    表現層框架Struts/Tapestry/JSF架構比較

    討論

    posted @ 2006-11-07 19:38 GoodtigerZhao 閱讀(274) | 評論 (0)編輯 收藏
     
    主站蜘蛛池模板: 91亚洲国产成人久久精品网站| 久久久久亚洲爆乳少妇无| 久久99亚洲网美利坚合众国| a级大片免费观看| 国产偷国产偷亚洲清高动态图| 老司机精品免费视频| 久久亚洲中文字幕精品一区| 国产成人无码免费网站| 国产日产亚洲系列| 青青草原1769久久免费播放| 亚洲av无码不卡一区二区三区 | 欧洲精品码一区二区三区免费看| 日韩免费无砖专区2020狼| 成人亚洲国产精品久久| 亚洲不卡无码av中文字幕| 特级aa**毛片免费观看| 亚洲中文字幕久久精品无码APP| 插鸡网站在线播放免费观看| 亚洲天堂一区二区| 在线看片免费不卡人成视频| 亚洲av成人片在线观看| 亚洲精品无码久久久| 成人爽a毛片免费| 国产成人亚洲合集青青草原精品| 国产精品二区三区免费播放心 | 亚洲成A人片在线播放器| 国产免费午夜a无码v视频| 51午夜精品免费视频| 久久精品亚洲日本佐佐木明希| 日本在线高清免费爱做网站| 亚洲精品第一国产综合亚AV| 亚洲男人在线无码视频| 日韩在线永久免费播放| 亚洲欧美日韩久久精品| 亚洲自偷自偷偷色无码中文| 91青青青国产在观免费影视 | 国产精品福利片免费看| 亚洲黄色在线观看网站| 免费又黄又爽又猛的毛片| 久久国产精品一区免费下载| 亚洲一区二区观看播放|