2006年11月8日

          使用 JSF 架構(gòu)進(jìn)行設(shè)計(jì)

          探索 JavaServer Faces 框架中使用的設(shè)計(jì)模式

          ?
          ??
          ?

          未顯示需要 JavaScript 的文檔選項(xiàng)


          拓展 Tomcat 應(yīng)用

          下載 IBM 開(kāi)源 J2EE 應(yīng)用服務(wù)器 WAS CE 新版本 V1.1


          級(jí)別: 中級(jí)

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

          2006 年 1 月 04 日

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

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

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

          設(shè)計(jì)模式和 JavaServer Faces (JSF) 技術(shù)

          首先簡(jiǎn)要地介紹一下模式和 JSF 框架。

          • 模式。設(shè)計(jì)模式是對(duì)問(wèn)題和解決方案進(jìn)行抽象的普遍適用的方法。因?yàn)槟J绞撬虚_(kāi)發(fā)人員和架構(gòu)師公認(rèn)的,所以模式可以節(jié)約時(shí)間和資源。用外行話來(lái)說(shuō),模式就是關(guān)于某個(gè)人所共知的問(wèn)題的經(jīng)過(guò)驗(yàn)證的解決方案。模式可以重用,重用使得解決方案更健壯。
          • Java Server Faces。 JSF 體系結(jié)構(gòu)是一種 Web 應(yīng)用程序框架。它是 Java Community Process (JCP) 推動(dòng)的,有望成為 Web 應(yīng)用程序開(kāi)發(fā)的標(biāo)準(zhǔn)框架。目前用于開(kāi)發(fā) Web 應(yīng)用程序的框架有 50 多個(gè),這說(shuō)明迫切需要實(shí)現(xiàn)框架的標(biāo)準(zhǔn)化,這正是 JSF 框架的目標(biāo)!

          ?

          ?
          ?


          深入剖析 JSF 模式

          現(xiàn)在我們來(lái)討論 JSF 體系結(jié)構(gòu)中的各種設(shè)計(jì)模式。本文將詳細(xì)討論 Singleton、Model-View-Controller、Factory Method、State、Composite、Decorator、Strategy、Template Method 和 Observer 設(shè)計(jì)模式。我將分析每種模式的用途及其在 JSF 框架中的作用。



          ?
          ?


          Singleton 模式

          Singleton 模式的目的是保證類只有一個(gè)實(shí)例被加載,該實(shí)例提供一個(gè)全局訪問(wèn)點(diǎn)。當(dāng)啟動(dòng)具有 JSF 支持的 Web 應(yīng)用程序時(shí),Web 容器初始化一個(gè) FacesServlet 實(shí)例。在這個(gè)階段,F(xiàn)acesServlet 對(duì)每個(gè) Web 應(yīng)用程序?qū)嵗?Application 和 LifeCycle 實(shí)例一次。這些實(shí)例就采用眾所周知的 Singleton 模式,通常只需要該類型的一個(gè)實(shí)例。

          使用 JSF 的 Web 應(yīng)用程序只需要 Application 和 LifeCycle 類的一個(gè)實(shí)例。LifeCycle 管理多個(gè) JSF 請(qǐng)求的整個(gè)生命期。因?yàn)槠錉顟B(tài)和行為在所有請(qǐng)求之間共享,這些對(duì)象采用 Singleton 模式合情合理。LifeCycle 維護(hù)的 PhaseListeners 也是 Singleton 模式的。PhaseListeners 由所有 JSF 請(qǐng)求共享。在 JSF 框架中可以廣泛使用 Singleton 模式,以減少內(nèi)存占用和提供對(duì)象的全局訪問(wèn)。NavigationHandler(用于確定請(qǐng)求的邏輯結(jié)果)和 ViewHandler(用于創(chuàng)建視圖)也是使用 Singleton 模式的例子。




          ?
          ?


          Model-View-Controller (MVC)

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

          FacesServlet 是 JSF 框架中所有用戶請(qǐng)求都要經(jīng)過(guò)的控制器元素。FacesServlet 分析用戶請(qǐng)求,使用托管 bean 對(duì)模型調(diào)用各種動(dòng)作。后臺(tái)(backing)或托管(managed)bean 就是該模型的例子。JSF 用戶界面(UI)組件是視圖層的例子。MVC 模式把任務(wù)分解給具有不同技能的開(kāi)發(fā)人員,使這些任務(wù)能夠同時(shí)進(jìn)行,這樣 GUI 設(shè)計(jì)人員就可以使用豐富的 UI 組件創(chuàng)建 JSF 頁(yè)面,同時(shí)后端開(kāi)發(fā)人員可以創(chuàng)建托管 bean 來(lái)編寫(xiě)專門(mén)的業(yè)務(wù)邏輯代碼。




          ?
          ?


          Factory Method 模式

          Factory Method 模式的目的是定義一個(gè)用于創(chuàng)建對(duì)象的接口,但是把對(duì)象實(shí)例化推遲到子類中。在 JSF 體系結(jié)構(gòu)中,F(xiàn)actory Method 模式被用于創(chuàng)建對(duì)象。LifeCycleFactory 是一個(gè)創(chuàng)建和返回 LifeCycle 實(shí)例的工廠對(duì)象。LifeCycleFactory 的 getLifeCycle (String LifeCycleId) 方法采用 Factory Method 模式,根據(jù) LifeCycleId 創(chuàng)建(如果需要)并返回 LifeCycle 實(shí)例。自定義的 JSF 實(shí)現(xiàn)可以重新定義 getLifeCycle 抽象方法來(lái)創(chuàng)建自定義的 LifeCycle 實(shí)例。默認(rèn)的 JSF 實(shí)現(xiàn)提供默認(rèn)的 LifeCycle 實(shí)例。此外,對(duì)于每個(gè) JSF 請(qǐng)求,F(xiàn)acesServlet 都從 FacesContextFactory 得到 FacesContext。FacesContextFactory 是一個(gè)抽象類,公開(kāi)了 getFacesContext API,JSF 實(shí)現(xiàn)提供了 FacesContextFactory 和 getFacesContext API 的具體實(shí)現(xiàn)。這是另外一個(gè)使用 Factory Method 模式的例子,具體的 FacesContextFactory 實(shí)現(xiàn)創(chuàng)建 FacesContext 對(duì)象。




          ?
          ?


          State 模式

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

          在 State 模式中,LifeCycle 把 FacesContext 對(duì)象傳遞給 phase。每個(gè)階段或狀態(tài)改變傳遞給它的上下文信息,然后設(shè)置 FacesContext 本身中的標(biāo)志表明下一個(gè)可能的步驟。JSF 實(shí)現(xiàn)在每個(gè)步驟中改變其行為。每個(gè)階段都可以作為下一個(gè)階段的起因。FacesContext 有兩種標(biāo)志 renderResponse 和 responseComplete 可以改變執(zhí)行的順序。每個(gè)步驟執(zhí)行完成后,LifeCycle 檢查上一階段是否設(shè)置了這些標(biāo)志。如果設(shè)置了 responseComplete,LifeCycle 則完全放棄請(qǐng)求的執(zhí)行。如果經(jīng)過(guò)某個(gè)階段后設(shè)置了 renderResponse 標(biāo)志,JSF 就會(huì)跳過(guò)剩下的階段而直接進(jìn)入 Render Response 階段。如果這兩個(gè)標(biāo)志都沒(méi)有設(shè)置,LifeCycle 就會(huì)按順序繼續(xù)執(zhí)行下一步。




          ?
          ?


          Composite 模式

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




          ?
          ?


          Decorator 模式

          Decorator 模式的目的是不通過(guò)子類化動(dòng)態(tài)擴(kuò)展對(duì)象的行為。JSF 框架有很多擴(kuò)展點(diǎn)(即可插入機(jī)制)。JSF 實(shí)現(xiàn)可使用 Decorator 模式替換默認(rèn)的 PropertyResolver、VariableResolver、ActionListener、NavigationHandler、ViewHandler 或 StateManager。通常自定義實(shí)現(xiàn)接受通過(guò)構(gòu)造函數(shù)傳遞給它的默認(rèn)實(shí)現(xiàn)的引用。自定義實(shí)現(xiàn)僅僅改寫(xiě)功能的一個(gè)子集,而將其他功能委托給默認(rèn)實(shí)現(xiàn)。如果希望實(shí)現(xiàn)自定義的 ViewHandler,改寫(xiě)默認(rèn) ViewHandler 實(shí)現(xiàn)的 calculateLocale 方法,可以像 清單 1 那樣編寫(xiě) 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 模式使用委托實(shí)現(xiàn)模型呈現(xiàn) UI 組件。JSF 技術(shù)支持兩種呈現(xiàn)模型。在直接實(shí)現(xiàn)模型中,UI 組件對(duì)收到的請(qǐng)求中的數(shù)據(jù)進(jìn)行解碼,然后編碼這些數(shù)據(jù)進(jìn)行顯示。在委托實(shí)現(xiàn)模型中,解碼和編碼操作委托給和組建關(guān)聯(lián)的專門(mén)呈現(xiàn)器。后一種模型利用了 Strategy 設(shè)計(jì)模式,比直接實(shí)現(xiàn)更靈活。在 Strategy 模式中,將不同的算法封裝在單獨(dú)的對(duì)象中,從而可以動(dòng)態(tài)地改變算法。JSF 實(shí)現(xiàn)可以用已有的 renderkit 實(shí)例注冊(cè)另外的呈現(xiàn)器,當(dāng)應(yīng)用程序啟動(dòng)的時(shí)候,JSF 實(shí)現(xiàn)讀取配置文件將這些呈現(xiàn)器和 UI 組件聯(lián)系在一起。




          ?
          ?


          Template Method 模式

          Template Method 模式的目的是將變化的步驟推遲到子類中,而在父類中定義那些固定的算法步驟。JSF 框架通過(guò) PhraseListeners 展現(xiàn)了 Template Method 模式提供的功能。采用 Template Method(或者 “hook”)使得 Web 作者可以為不同階段之間的可選步驟提供實(shí)現(xiàn),而主要階段仍然和 JSF 框架的定義一致。JSF 框架提供了 PhaseListeners,概念上類似于 Template Method 模式中的可變步驟。JSF 框架有六個(gè)預(yù)定義的階段,在每個(gè)階段之間,Web 作者可以實(shí)現(xiàn) PhaseListeners 來(lái)提供類似于 Template Method hook 的 hook。事實(shí)上,這種結(jié)構(gòu)比 Template Method 模式更具有擴(kuò)展性。可以通過(guò)注冊(cè) PhraseId 為 ANY_PHRASE 的 PhaseListener 在每個(gè)階段后提供 hook。如果 PhaseId 是 ANY_PHASE,JSF 實(shí)現(xiàn)就會(huì)在每個(gè)階段之前和之后調(diào)用該 PhaseListener。JSF 框架中的實(shí)現(xiàn)略有不同,因?yàn)榭梢愿緵](méi)有 PhaseListener,但是在 Template Method 模式中,子類通常重新定義父類中抽象的可變步驟。




          ?
          ?


          Observer 模式

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




          ?
          ?


          結(jié)束語(yǔ)

          JSF 框架利用了 Singleton、Model-View-Controller、Factory Method、State、Composite、Decorator、Strategy、Template Method 和 Observer 設(shè)計(jì)模式。因?yàn)樗捏w系結(jié)構(gòu)建立在已經(jīng)驗(yàn)證的設(shè)計(jì)模式的基礎(chǔ)上,這是一個(gè)健壯的框架,模式在 JSF 框架中得到了很好的利用。



          參考資料

          學(xué)習(xí)

          獲得產(chǎn)品和技術(shù)

          討論


          關(guān)于作者

          Anand Joshi 的照片

          Anand 是一位 Sun 認(rèn)證的企業(yè)架構(gòu)師,幾年來(lái)一直研究 Web 技術(shù)。他對(duì) WebSphere 管理控制臺(tái)應(yīng)用程序的設(shè)計(jì)和開(kāi)發(fā)做了多方面的貢獻(xiàn)。Anand 曾經(jīng)在 IBM 美國(guó)工作過(guò)幾年,目前在 IBM 印度工作。


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

          2006年11月7日


          JSF是一種新的用于構(gòu)架j2ee應(yīng)用用戶界面的技術(shù),它尤其適合于基于MVC架構(gòu)的應(yīng)用中。雖已有很多文章介紹過(guò)了JSF,然而它們大多從理論高度來(lái)介紹JSF而不是面向于實(shí)際應(yīng)用。目前對(duì)于實(shí)際應(yīng)用,JSF仍有很多問(wèn)題沒(méi)有解決,例如:如何使JSF適應(yīng)于MVC整體構(gòu)架中?如何將JSF與其他Java 框架整合起來(lái)?是否應(yīng)該將業(yè)務(wù)邏輯放置在JSF的backing beans中?如何處理JSF中的安全機(jī)制?更為重要的是如何利用JSF構(gòu)架現(xiàn)實(shí)世界的Web應(yīng)用?

          本文將涉及到上面的這些問(wèn)題,它將演示如何將JSF、Spring和Hibernate整合在一起,構(gòu)架出一個(gè)名為JCatalog的在線產(chǎn)品價(jià)目系統(tǒng)
          。利用該Demo,本文涵蓋了Web應(yīng)用開(kāi)發(fā)的每一個(gè)階段,包括需求收集、分析,技術(shù)選擇,系統(tǒng)架構(gòu)和實(shí)現(xiàn)。本文討論了在JCatalog中涉及到的各種技術(shù)的優(yōu)點(diǎn)和缺點(diǎn)并展示了一些關(guān)鍵部分的設(shè)計(jì)方法。

          本文的對(duì)象是從事基于J2ee的Web應(yīng)用架構(gòu)人員和開(kāi)發(fā)人員,它并不是對(duì)JSF、SpringFramework和Hibernate的簡(jiǎn)單介紹,如果對(duì)這些領(lǐng)域不甚了解,請(qǐng)參看相關(guān)資源。

          該范例的功能需求
          JCatalog是一個(gè)現(xiàn)實(shí)世界的Web應(yīng)用,我首先描述JCatalog的需求,在通篇的技術(shù)決策和架構(gòu)設(shè)計(jì)時(shí)都將涉及到本部分。

          在設(shè)計(jì)Web應(yīng)用的第一階段是收集系統(tǒng)的功能需求,范例應(yīng)用是一個(gè)典型的電子商務(wù)應(yīng)用系統(tǒng),用戶可以瀏覽產(chǎn)品的catalog并查看產(chǎn)品的詳細(xì)情況,而管理員可以管理產(chǎn)品的catalog。通過(guò)增加一些其他功能,如inventory管理和訂單處理等,該應(yīng)用可成為一個(gè)成熟的電子商務(wù)系統(tǒng)。

          Use cases
          Use-case分析被用來(lái)展示范例應(yīng)用的功能需求,圖1就是該應(yīng)用的use-case圖。
          ????


          use-case圖用于表示系統(tǒng)中的actors以及可能進(jìn)行的operations,在該應(yīng)用中將有七個(gè)use-case,用戶能夠?yàn)g覽產(chǎn)品 catalog和查看產(chǎn)品的詳細(xì)情況,一旦用戶登錄到系統(tǒng)中,她將成為管理員,從而可以創(chuàng)建新的產(chǎn)品,編輯已存在的產(chǎn)品或者刪除老的產(chǎn)品等。

          Business rules
          JCatalog必須符合以下business rules:
          • 每個(gè)產(chǎn)品必須具有唯一的ID
          • 每個(gè)產(chǎn)品必須屬于至少一個(gè)category
          • 產(chǎn)品ID一旦創(chuàng)立不得修改
          Assumptions
          我們?cè)谙到y(tǒng)的設(shè)計(jì)和實(shí)現(xiàn)中做以下假定:
          • 英語(yǔ)講是缺省語(yǔ)言,且不需事先國(guó)際化
          • 在Catalog不講不會(huì)超過(guò)500個(gè)產(chǎn)品
          • catalog將不會(huì)被頻繁的修改
          Page flow
          圖2顯示了所有的JCatalog的pages以及它們之間的transitions關(guān)系:
          ????


          該應(yīng)用中存在兩組pages:公開(kāi)的internet和用于管理的intranet,其中intranet只能被那些成功登錄到系統(tǒng)的用戶訪問(wèn)。 ProductSummary不作為一個(gè)單獨(dú)的page展示給用戶,它顯示在Catalog page中的frame中。ProductList只對(duì)管理員可視,它包含用于創(chuàng)建、編輯和刪除產(chǎn)品的鏈接。

          圖3是一個(gè)Catalog頁(yè)面的示意圖,理想狀況下,在需求文檔中應(yīng)該包含每一頁(yè)的詳細(xì)示意圖。
          ????????


          構(gòu)架設(shè)計(jì)
          Web應(yīng)用開(kāi)發(fā)的下一個(gè)階段是構(gòu)架設(shè)計(jì),它包括將應(yīng)用劃分為多個(gè)功能組件并將這些組件分割組合成層,高層的構(gòu)架設(shè)計(jì)應(yīng)該中立于所選用的特定技術(shù)。

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

          client層是使用和表示數(shù)據(jù)模型的地方,對(duì)于一個(gè)Web應(yīng)用,client層通常是瀏覽器,基于瀏覽器的瘦客戶端不包含任何表示邏輯,它依賴于
          presentation 層。

          presentation層將business-logic層的服務(wù)展示給用戶,它應(yīng)知道如何處理用戶的請(qǐng)求,如何同business-logic層交互,并且知道如何選擇下一個(gè)視圖顯示給用戶。

          business-logic層包含應(yīng)用的business objects和business services。它接受來(lái)在于presentation層的請(qǐng)求、基于請(qǐng)求處理業(yè)務(wù)邏輯。業(yè)務(wù)邏輯層組件將受益于系統(tǒng)級(jí)的服務(wù),如安全管理、事務(wù)管理和資源管理等。

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

          應(yīng)用的數(shù)據(jù)被保存在EIS層中,它包括關(guān)系數(shù)據(jù)庫(kù)、面向?qū)ο髷?shù)據(jù)庫(kù)和以及遺留系統(tǒng)等。

          JCatalog的構(gòu)架設(shè)計(jì)
          圖4顯示了JCatalog的構(gòu)架設(shè)計(jì)以及如何應(yīng)用于多層構(gòu)架系統(tǒng)中。
          ?


          該應(yīng)用采用了多層非分布式的構(gòu)架,圖4展示了系統(tǒng)的分層以及每一層中選擇的技術(shù),它同時(shí)又是該范例的部署圖,它的presentation、 business-logic和integration層將存在于同一個(gè)web容器中。定義良好的接口將孤立每一層的職責(zé),這一架構(gòu)使得應(yīng)用更為簡(jiǎn)單和更好的伸縮性。

          對(duì)于presentation層,經(jīng)驗(yàn)表明,最好的方法是選擇已存在的并已得到證明了的Web應(yīng)用框架,而不是自己去設(shè)計(jì)和開(kāi)發(fā)新的框架。我們擁有多個(gè)可選擇的框架,如Struts,WebWork和JSF等,在JCatalog中,我們選擇采用JSF。

          EJB和POJO都可以用來(lái)創(chuàng)建業(yè)務(wù)邏輯層,如果應(yīng)用是分布式的,采用具有remote接口的EJB是一個(gè)好的選擇;由于JCatalog是一個(gè)典型的不需要遠(yuǎn)程訪問(wèn)的Web應(yīng)用,因此選用POJO,并充分利用Spring Framework的幫助,將是實(shí)現(xiàn)業(yè)務(wù)邏輯層的更好選擇。

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

          presentation 層和JSF
          表示層的功能是收集用戶的輸入、展示數(shù)據(jù)、控制頁(yè)面導(dǎo)航并將用戶的輸入傳遞給業(yè)務(wù)邏輯層,表示層同時(shí)需要驗(yàn)證用戶的輸入以及維護(hù)應(yīng)用的session狀態(tài)。在下面幾部分中,我將討論表示層設(shè)計(jì)時(shí)的考慮和模式,并說(shuō)明選擇JSF作為JCatalog表示層的原因。

          MVC
          MVC是Java-Blueprints推薦的架構(gòu)設(shè)計(jì)模式,MVC將幾個(gè)方面分離開(kāi)來(lái),從而減少代碼的重復(fù),它以控制為中心并使得應(yīng)用更具擴(kuò)展性。MVC同時(shí)可幫助具有不同技能的用戶更關(guān)注于自己的技能,通過(guò)定義良好的接口進(jìn)行相互合作。MVC是表示層的架構(gòu)設(shè)計(jì)模式。

          JSF
          JSF是Web應(yīng)用的服務(wù)器端用戶組件框架,它包含以下API:表示UI組件、管理它們的狀態(tài)、處理事件、服務(wù)器端驗(yàn)證、數(shù)據(jù)轉(zhuǎn)換、定義頁(yè)面導(dǎo)航、支持國(guó)際化,并為這些特性提供擴(kuò)展能力。它同時(shí)包括兩個(gè)JSP的tag庫(kù)以在JSP頁(yè)面中表示UI組件,以及將組件wire為服務(wù)器端對(duì)象。

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

          JSF backing beans是JSF的Model層,此外,它同樣包含actions,action是controller層的擴(kuò)展,用于將用戶的請(qǐng)求委派給業(yè)務(wù)邏輯層。這里請(qǐng)注意,從整體的應(yīng)用構(gòu)架看,業(yè)務(wù)邏輯層也被稱為model層。包含JSF標(biāo)簽的JSP頁(yè)面是表示層,F(xiàn)aces Servlet提供了controller的功能。

          為什么選用JSF?

          JSF不僅僅是另外一個(gè)Web框架,下面這些特性是JSF區(qū)別于其他Web框架之所在:
          • 類Swing的面向?qū)ο蟮腤eb應(yīng)用開(kāi)發(fā):服務(wù)器端有狀態(tài)的UI組件模型,配合event listeners和handlers,促進(jìn)了面向?qū)ο蟮腤eb應(yīng)用開(kāi)發(fā)。
          • backing-bean管理: backing bean是與頁(yè)面中使用的UI組件相關(guān)聯(lián)的javabean組件,backing-bean管理將UI組件對(duì)象的定義同執(zhí)行應(yīng)用相關(guān)處理和擁有數(shù)據(jù)的對(duì)象分離開(kāi)來(lái)。JSF在合適的范圍內(nèi)保存和管理這些backing-bean實(shí)例。
          • 可擴(kuò)展的UI模型:JSF的UI模型是可配置的、可重用的,用以構(gòu)建JSF應(yīng)用的用戶界面。你可以通過(guò)擴(kuò)展標(biāo)準(zhǔn)的UI組件來(lái)開(kāi)發(fā)出更為復(fù)雜的組件,例如菜單條、樹(shù)組件等。
          • 靈活的rendering模型:renderer分離了UI組件的功能和顯示,多個(gè)renderers可創(chuàng)建和用來(lái)為同一客戶端或不同的客戶端定義不同的顯示。
          • 可擴(kuò)展的轉(zhuǎn)換和驗(yàn)證模型:基于標(biāo)準(zhǔn)的converter和validator,你可以開(kāi)發(fā)出自己的可提供更好的模型保護(hù)的converter和validator。
          盡管如此,JSF目前尚未成熟,隨同JSF發(fā)布的 components、converters和validators都是最基礎(chǔ)的,而且per-component驗(yàn)證模型不能處理components 和validators間的many-to-many驗(yàn)證。此外,JSF標(biāo)簽不能與JSTL間無(wú)縫的整合在一起。

          在下面的章節(jié)中,我將討論幾個(gè)在JCatalog實(shí)現(xiàn)中的關(guān)鍵部分和設(shè)計(jì)決策。我首先解釋managed bean的定義和使用以及JSF中的backing bean,然后,我將說(shuō)明如何處理安全、分頁(yè)、caching、file upload、驗(yàn)證以及錯(cuò)誤信息定制。

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

          backing bean定義了與頁(yè)面中使用的UI組件相關(guān)聯(lián)的屬性和處理邏輯。每一個(gè)backing-bean屬性邦定于一個(gè)組件實(shí)例或某實(shí)例的value。一個(gè) backing-bean同時(shí)定義了一組執(zhí)行組件功能的方法,例如驗(yàn)證組件的數(shù)據(jù)、處理組件觸發(fā)的事件、實(shí)施與組件相關(guān)的導(dǎo)航等。

          一個(gè)典型的JSF應(yīng)用將其中的每個(gè)頁(yè)面和一個(gè)backing-bean結(jié)合起來(lái),然而在現(xiàn)實(shí)應(yīng)用中,強(qiáng)制的執(zhí)行這種one-on-one的關(guān)系不是一種理想的解決方案,它可能會(huì)導(dǎo)致代碼重復(fù)等問(wèn)題。在現(xiàn)實(shí)的應(yīng)用中,多個(gè)頁(yè)面可以共享一個(gè)backing-bean,例如在JCatalog中, CreateProduct和EditProduct將共享同一個(gè)ProductBean定義。

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

          對(duì)比于struts中的ActionForm和Action,利用JSF中的backing-bean進(jìn)行開(kāi)發(fā)將能更好的遵循面向?qū)ο蠓椒ǎ粋€(gè) backing-bean不僅包含view數(shù)據(jù),而且還包含與這些數(shù)據(jù)相關(guān)的行為,而在struts中,Action和ActionForm分別包含數(shù)據(jù)和邏輯。

          我們都應(yīng)該聽(tīng)說(shuō)過(guò)domain object model,那么,domain object model和view對(duì)象之間有什么區(qū)別呢?在一個(gè)簡(jiǎn)單的Web應(yīng)用中,一個(gè)domain object model能夠橫穿所有層中,而在復(fù)雜的應(yīng)用中,需要用到一個(gè)單獨(dú)的view對(duì)象模型。domain object model應(yīng)該屬于業(yè)務(wù)邏輯層,它包含業(yè)務(wù)數(shù)據(jù)和與特定業(yè)務(wù)對(duì)象相關(guān)的業(yè)務(wù)邏輯;一個(gè)view對(duì)象包含presentation-specific的數(shù)據(jù)和邏輯。將view對(duì)象從domain object model中分離出來(lái)的缺點(diǎn)是在這兩個(gè)對(duì)象模型之間必將出現(xiàn)數(shù)據(jù)映射。在JCatalog中,ProductBeanBuilder和 UserBeanBuilder利用reflection-based Commons BeanUtils來(lái)實(shí)現(xiàn)數(shù)據(jù)映射。

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

          分頁(yè)
          該應(yīng)用中的Catalog頁(yè)面需要分頁(yè),表示層可用來(lái)處理分頁(yè),即它取出所有的數(shù)據(jù)并保存在這一層;分頁(yè)同樣可在business-logic層、 integration層、甚至EIS層中實(shí)現(xiàn)。由于在JCatalog中假設(shè)不超過(guò)500個(gè)產(chǎn)品,因此所有的產(chǎn)品信息能存放在一個(gè)user session中,我們將分頁(yè)邏輯放在了ProductListBean中,與分頁(yè)相關(guān)的參數(shù)將通過(guò)JSF managed-bean工具配置。

          Caching
          Caching是提高Web應(yīng)用性能的最重要技術(shù)之一,在應(yīng)用構(gòu)建中的很多層中都可以實(shí)現(xiàn)caching。JSF managed-bean工具可以使在表示層實(shí)現(xiàn)caching非常容易。通過(guò)改變一個(gè)managed bean的范圍,這個(gè)managed bean中包含的數(shù)據(jù)可以在不同的范圍內(nèi)緩存。

          范例應(yīng)用中采用了兩級(jí)caching,第一級(jí)caching存在于業(yè)務(wù)邏輯層,CachedCatalogServiceImpl類維護(hù)了一個(gè)所有產(chǎn)品和目錄的讀寫(xiě)cache,Spring將該類作為一個(gè)singleton service bean來(lái)管理,所以,一級(jí)cache是一個(gè)應(yīng)用范圍的讀寫(xiě)cache。

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

          File upload
          目前的JSF Sun參考實(shí)現(xiàn)中不支持file upload。Struts雖已具有非常不錯(cuò)的file upload能力,然而要想使用這一特性需要Struts-Faces整合庫(kù)。在JCatalog中,一個(gè)圖像與一個(gè)產(chǎn)品相關(guān)聯(lián),在一個(gè)用戶創(chuàng)建了新的產(chǎn)品后,她必須將相應(yīng)的圖片上傳,圖片將保存在應(yīng)用服務(wù)器的文件系統(tǒng)里,產(chǎn)品的ID就是圖像名稱。

          范例應(yīng)用中采用、Servlet和Jakarta Common的file-upload API來(lái)實(shí)現(xiàn)簡(jiǎn)單的文件上傳功能,該方法包含兩個(gè)參數(shù):圖像路徑和圖像上傳結(jié)果頁(yè)面。它們將通過(guò)ApplicationBean來(lái)配置,詳細(xì)內(nèi)容請(qǐng)參看 FileUploadServlet類。

          Validation
          JSF中發(fā)布的標(biāo)準(zhǔn)validator是非常基礎(chǔ)的,無(wú)法滿足現(xiàn)實(shí)的需要,但很容易開(kāi)發(fā)出自己的JSF validator,在范例中,我開(kāi)發(fā)了SelectedItemsRange validator,它用來(lái)驗(yàn)證UISelectMany組件中選擇的數(shù)量:

          ?
          ? ??
          ????
          ?


          詳細(xì)情況請(qǐng)參看范例。

          定制錯(cuò)誤信息
          在JSF中,你可以為converters和validators創(chuàng)建resource bundle和定制錯(cuò)誤信息,一個(gè)resource bundle可在faces-config.xml中創(chuàng)建:

          ? catalog.view.bundle.Messages

          并將錯(cuò)誤信息的key-value對(duì)加到Message.properties文件中:

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

          業(yè)務(wù)邏輯層和Spring Framework
          業(yè)務(wù)對(duì)象和業(yè)務(wù)服務(wù)存在于業(yè)務(wù)邏輯層中,一個(gè)業(yè)務(wù)對(duì)象不僅包含數(shù)據(jù),而且包含相應(yīng)的邏輯,在范例應(yīng)用中包含三個(gè)業(yè)務(wù)對(duì)象:Product、Category和User。

          業(yè)務(wù)服務(wù)與業(yè)務(wù)對(duì)象交互并提供更高級(jí)的業(yè)務(wù)邏輯,需要首先定義一個(gè)正式的業(yè)務(wù)接口,它是直接與終端用戶交互的服務(wù)接口。在JCatalog中,通過(guò)在 Spring Framework幫助下的POJO實(shí)現(xiàn)業(yè)務(wù)邏輯層,其中共有兩個(gè)業(yè)務(wù)服務(wù):CatalogService包含Catalog管理相關(guān)的業(yè)務(wù)邏輯, UserService中包含User管理邏輯。

          Spring是基于IoC概念的框架,在范例應(yīng)用中用到的Spring特性包括:
          • Bean management with application contexts:Spring可以有效地組織我們的中間層對(duì)象,它能夠消除singleton的proliferation,并易于實(shí)現(xiàn)良好的面向?qū)ο缶幊谭椒ǎ础熬幊痰浇涌凇薄?/font>
          • Declarative Transaction management: Spring利用AOP實(shí)現(xiàn)事務(wù)管理,而無(wú)需借助于EJB容器,利用這種方法,事務(wù)管理可以用于任何POJO中。Spring的事務(wù)管理不局限于JTA,而是可以采用不同的事務(wù)策略,在范例應(yīng)用中,我們將使用declarative transaction management with Hibernate transaction。
          • Data-access exception hierarchy:Spring提供了非常好的異常來(lái)代替SQLException,為利用Spring的異常,必須在Spring的配置文件中定義以下異常轉(zhuǎn)換:
          ???????
          ? ? ? ? ?
          ?????????????
          ?????????

          ???????


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

          Integration層和Hibernate
          Hibernate是一個(gè)開(kāi)源的ORM框架,它可以支持所有主流SQL數(shù)據(jù)庫(kù)系統(tǒng),Hibernate的查詢語(yǔ)言為對(duì)象和關(guān)系架起了非常好的橋梁。Hibernate提供了強(qiáng)大的功能以實(shí)現(xiàn):數(shù)據(jù)讀取和更新、事務(wù)管理、數(shù)據(jù)連接池、查詢和實(shí)體關(guān)系管理等。

          Data Access Ojbect(DAO)
          JCatalog中采用了Dao模式,該模式抽象和封裝了所有對(duì)數(shù)據(jù)源的訪問(wèn),該應(yīng)用中包括兩個(gè)DAO接口:CatalogDao和UserDao,它們相應(yīng)的實(shí)現(xiàn)HibernateCatalogDaoImpl和HibernateUserDAoImpl包含了Hibernate特定的邏輯來(lái)實(shí)現(xiàn)數(shù)據(jù)的管理和持久化。

          實(shí)現(xiàn)
          現(xiàn)在我們來(lái)看看如何將上面討論的這些東西包裝在一起以實(shí)現(xiàn)JCatalog,你可以從這個(gè)地址下載源碼:source code

          數(shù)據(jù)庫(kù)設(shè)計(jì)
          我們?yōu)樵摲独龖?yīng)用創(chuàng)建了包含4個(gè)表的數(shù)據(jù)庫(kù),如圖5所示:


          類設(shè)計(jì)
          圖6顯示了JCatalog的類圖


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

          Wire everything up
          由于篇幅所限,我們僅舉例說(shuō)明,范例中use case CreateProduct展示了如何裝配和構(gòu)建應(yīng)用,在詳細(xì)講述細(xì)節(jié)前,我們利用sequence圖(圖7)來(lái)說(shuō)明所有層的end-tp-end整合。



          表示層
          表示層實(shí)現(xiàn)包括創(chuàng)建JSP頁(yè)面、定義頁(yè)導(dǎo)航、創(chuàng)建和配置backing bean以及將JSF與業(yè)務(wù)邏輯層整合。
          • JSP page:createProduct.jsp是用來(lái)創(chuàng)建新產(chǎn)品的頁(yè)面,它包含UI組件并將組件打包成ProductBean,ValidateItemsRange標(biāo)簽用來(lái)驗(yàn)證用戶選擇的種類數(shù)量,對(duì)每一個(gè)產(chǎn)品至少要有一個(gè)種類被選中。
          • 頁(yè)面導(dǎo)航:應(yīng)用中的導(dǎo)航被定義在應(yīng)用的配置文件faces-navigation.xml中,CreateProduct的導(dǎo)航準(zhǔn)則如下:

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


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

          • Backing bean:ProductBean不僅包含有將數(shù)據(jù)映射到頁(yè)面上的UI組件的屬性,還包括三個(gè)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}
          ??

          • ?表示層和業(yè)務(wù)邏輯層之間的整合: ServiceLocator抽象了查找服務(wù)的邏輯,在范例應(yīng)用中,ServiceLocator被定義為一個(gè)接口,該接口實(shí)現(xiàn)為一個(gè)JSF的 managed bean,即ServiceLocatorBean,它將在Spring的application context中尋找服務(wù):
          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);
          業(yè)務(wù)邏輯層
          • 業(yè)務(wù)對(duì)象:由于采用Hibernate提供持久化,因此Product和Category兩個(gè)業(yè)務(wù)對(duì)象需要為它們的所有field提供getter和setter。
          • 業(yè)務(wù)服務(wù):CatalogService接口中定義了所有的與Catalog management相關(guān)的服務(wù):
          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通過(guò)xml配置文件來(lái)映射業(yè)務(wù)對(duì)象和關(guān)系數(shù)據(jù)庫(kù),在JCatalog中,Product.hbm.xml表示了Product對(duì)象的映射,Category.hbm.xml則用來(lái)表示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>

          結(jié)論
          本文主要講述了如何將JSF與Spring、Hibernate整合在一起來(lái)構(gòu)建實(shí)際的Web應(yīng)用,這三種技術(shù)的組合提供了一個(gè)強(qiáng)大的Web應(yīng)用開(kāi)發(fā)框架。在Web應(yīng)用的高層設(shè)計(jì)中應(yīng)該采用多層構(gòu)架體系,JSF非常適合MVC設(shè)計(jì)模式以實(shí)現(xiàn)表示層,Spring可用在業(yè)務(wù)邏輯層中管理業(yè)務(wù)對(duì)象,并提供事物管理和資源管理等,Spring與Hibernate結(jié)合的非常出色,Hibernate是強(qiáng)大的O/R映射框架,它可以在integration層中提供最好的服務(wù)。

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

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

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

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

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

            struts重要的表單對(duì)象ActionForm是一種對(duì)象,它代表了一種應(yīng)用,這個(gè)對(duì)象中至少包含幾個(gè)字段,這些字段是Jsp頁(yè)面表單中的input字段,因?yàn)橐粋€(gè)表單對(duì)應(yīng)一個(gè)事件,所以,當(dāng)我們需要將事件粒度細(xì)化到表單中這些字段時(shí),也就是說(shuō),一個(gè)字段對(duì)應(yīng)一個(gè)事件時(shí),單純使用Struts就不太可能,當(dāng)然通過(guò)結(jié)合JavaScript也是可以轉(zhuǎn)彎實(shí)現(xiàn)的。

            而這種情況使用JSF就可以方便實(shí)現(xiàn),

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

            #{login.userId}表示從名為login的JavaBean的getUserId獲得的結(jié)果,這個(gè)功能使用struts也可以實(shí)現(xiàn),name="login" property="userId"

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

            JSF可以為組件提供兩種事件:Value Changed和 Action. 前者我們已經(jīng)在上節(jié)見(jiàn)識(shí)過(guò)用處,后者就相當(dāng)于struts中表單提交Action機(jī)制,它的JSF寫(xiě)法如下:

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

            從代碼可以看出,這兩種事件是通過(guò)Listerner這樣觀察者模式貼在具體組件字段上的,而Struts此類事件是原始的一種表單提交Submit觸發(fā)機(jī)制。如果說(shuō)前者比較語(yǔ)言化(編程語(yǔ)言習(xí)慣做法類似Swing編程);后者是屬于WEB化,因?yàn)樗莵?lái)自Html表單,如果你起步是從Perl/PHP開(kāi)始,反而容易接受Struts這種風(fēng)格。

          基本配置

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

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

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

            所以,從JSF的驅(qū)動(dòng)包組成看,其開(kāi)源基因也占據(jù)很大的比重,JSF是一個(gè)SUN伙伴們工業(yè)標(biāo)準(zhǔn)和開(kāi)源之間的一個(gè)混血兒。

            上述兩個(gè)地址下載的jar合并在一起就是JSF所需要的全部驅(qū)動(dòng)包了。與Struts的驅(qū)動(dòng)包一樣,這些驅(qū)動(dòng)包必須位于Web項(xiàng)目的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配置何其相似,簡(jiǎn)直一模一樣。

            正如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之間的流程關(guān)系,在faces-config.xml中,也有這樣的流程,我們具體解釋一下Navigation:

            在index.jsp中有一個(gè)事件:

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

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

            JSF有一個(gè)獨(dú)立的事件發(fā)生和頁(yè)面導(dǎo)航的流程安排,這個(gè)思路比struts要非常清晰。

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

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

          Ioc模式

            對(duì)于Userbean這樣一個(gè)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這段配置其實(shí)是將"me"賦值給name,將secret賦值給password,這是采取Ioc模式中的Setter注射方式

          Backing Beans

            對(duì)于一個(gè)web form,我們可以使用一個(gè)bean包含其涉及的所有組件,這個(gè)bean就稱為Backing Bean, Backing Bean的優(yōu)點(diǎn)是:一個(gè)單個(gè)類可以封裝相關(guān)一系列功能的數(shù)據(jù)和邏輯。

            說(shuō)白了,就是一個(gè)Javabean里包含其他Javabean,互相調(diào)用,屬于Facade模式或Adapter模式。


            對(duì)于一個(gè)Backing Beans來(lái)說(shuō),其中包含了幾個(gè)managed-bean,managed-bean一定是有scope的,那么這其中的幾個(gè)managed-beans如何配置它們的scope呢?

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

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

          UI界面

            JSF和Struts一樣,除了JavaBeans類之外,還有頁(yè)面表現(xiàn)元素,都是是使用標(biāo)簽完成的,Struts也提供了struts-faces.tld標(biāo)簽庫(kù)向JSF過(guò)渡。

            使用Struts標(biāo)簽庫(kù)編程復(fù)雜頁(yè)面時(shí),一個(gè)最大問(wèn)題是會(huì)大量使用logic標(biāo)簽,這個(gè)logic如同if語(yǔ)句,一旦寫(xiě)起來(lái),搞的JSP頁(yè)面象俄羅斯方塊一樣,但是使用JSF標(biāo)簽就簡(jiǎn)潔優(yōu)美:

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

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

            先寫(xiě)到這里,我會(huì)繼續(xù)對(duì)JSF深入比較下去,如果研究過(guò)Jdon框架的人,可能會(huì)發(fā)現(xiàn),Jdon框架的jdonframework.xml中service配置和managed-bean一樣都使用了依賴注射,看來(lái)對(duì)Javabean的依賴注射已經(jīng)迅速地成為一種新技術(shù)象征,如果你還不了解Ioc模式,趕緊補(bǔ)課。

          附Jsf核心教程一個(gè)JSF案例:login.rar

          相關(guān)討論:

          表現(xiàn)層框架Struts/Tapestry/JSF架構(gòu)比較

          討論

          posted @ 2006-11-07 19:38 GoodtigerZhao 閱讀(279) | 評(píng)論 (0)編輯 收藏
          僅列出標(biāo)題  
           
          主站蜘蛛池模板: 乌兰察布市| 临江市| 磴口县| 定结县| 林芝县| 佛冈县| 东城区| 朝阳县| 长泰县| 甘德县| 大港区| 时尚| 玉环县| 临江市| 大安市| 博野县| 乾安县| 长阳| 德钦县| 新宁县| 大田县| 涞水县| 龙里县| 汤原县| 阿克苏市| 上思县| 乳源| 望奎县| 林周县| 吉林市| 汉寿县| 馆陶县| 海丰县| 同仁县| 娄烦县| 顺昌县| 铜山县| 牡丹江市| 武平县| 广德县| 安义县|