hongyunxp

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            2 隨筆 :: 2 文章 :: 1 評論 :: 0 Trackbacks

          2007年5月20日 #

           

          摘要 面向方面編程(AOP)是面向對象編程(OOP)的一種擴展技術,能夠很好的解決橫切關注點問題以及相關的設計難題來實現松散耦合。Spring AOP 是 AOP 技術的一種實現。本文介紹了AOP 概念,然后詳細討論了如何利用Spring AOP 開發AOP 程序,最后展望了Spring AOP 的前景。

            關鍵詞 AOP Spring AOP Java

            引言

            AOP(Aspected Oriented Programming)是一種新興的編程技術。它可以解決OOP和過程化方法不能夠很好解決的橫切(crosscut)問題,如:事務、安全、日志等橫切關注。當未來系統變得越來越復雜,橫切關注點就成為一個打問題的時候,AOP就可以很輕松的解決橫切關注點這個問題,使得AOP編程成為。Spring 是基于J2EE的輕量級開源開發框架,其中Spring AOP組件實現了面向方面編程。

            AOP 概述

            面向方面編程 (AOP) 提供從另一個角度來考慮程序結構以完善面向對象編程(OOP)。 面向對象將應用程序分解成各個層次的對象,而AOP將程序分解成各個方面或者說關注點 。這使得可以模塊化諸如事務管理等這些橫切多個對象的關注點。

            1、AOP 基本概念

            方面(Aspect): 一個關注點的模塊化,這個關注點實現可能另外橫切多個對象。事務管理是J2EE應用中一個很好的橫切關注點例子。方面用Spring的 Advisor或攔截器實現。

            連接點(Joinpoint): 程序執行過程中明確的點,如方法的調 用或特定的異常被拋出。

            通知(Advice): 在特定的連接點,AOP框架執行的動作。各種類 型的通知包括“around”、“before”和“throws”通知。通知類型將在下面討論。許多AOP框架 包括Spring都是以攔截器做通知模型,維護一個“圍繞”連接點的攔截器鏈。

            切入點(Pointcut): 指定一個通知將被引發的一系列連接點 的集合。AOP框架必須允許開發者指定切入點:例如,使用正則表達式。

            引入(Introduction): 添加方法或字段到被通知的類。 Spring允許引入新的接口到任何被通知的對象。例如,你可以使用一個引入使任何對象實現 IsModified接口,來簡化緩存。

            目標對象(Target Object): 包含連接點的對象。也被稱作 被通知或被代理對象。

            AOP代理(AOP Proxy): AOP框架創建的對象,包含通知。 在Spring中,AOP代理可以是JDK動態代理或者CGLIB代理。

            織入(Weaving): 組裝方面來創建一個被通知對象。這可以在編譯時 完成(例如使用AspectJ編譯器),也可以在運行時完成。Spring和其他純Java AOP框架一樣, 在運行時完成織入。

            Spring AOP 介紹

            Spring的一個關鍵組件就是AOP框架。 Spring IoC容器(BeanFactory 和ApplicationContext)并不依賴于AOP, 這意味著如果你不需要使用,AOP可以不用,AOP完善了Spring IoC,使之成為一個有效的中間件解決方案。

            Spring AOP 是Spring 框架的重要組成部分,它實現了AOP聯盟約定的接口。Spring AOP 是由純Java開發完成的。Spring AOP 只實現了方法級別的連接點,在J2EE應用中,AOP攔截到方法級的操作已經足夠。OOP倡導的是基于setter/getter 的方法訪問,而非直接訪問域,而Spring 有足夠理由僅僅提供方法級的連接點。為了使控制反轉(IoC)很方便的使用到非常健壯、靈活的企業服務,則需要Spring AOP 的實現。Spring AOP 在運行時才創建Advice 對象。Spring AOP的優點如下:

            ·允許開發者使用聲明式企業服務,比如事務服務、安全性服務。

            ·開發者可以開發滿足業務需求的自定義方面。

            ·開發Spring AOP Advice 很方便,可以借助代理類快速搭建Spring AOP 應用。

            使用Spring AOP松散耦合

            1、創建通知

            為實現AOP,開發者需要開發AOP 通知(Advice)。AOP 通知(Advice) 包含了方面(Aspect)的邏輯。當創建一個Advice 對象的時候,你就編寫了實現橫切(cross-cutting)功能。 Spring 的連接點是用方法攔截器實現的,這就意味著你編寫的Spring AOP 通知將在方法調用的不同點組入進程序中。由于在調用一個方法時有幾個不同的時間點,Spring 可以在不同的時間點組入進程序。Spring AOP中,提供了四種通知的接口: MethodBeforeAdvice 用于在目標方法調用前觸發;AfterReturningAdvice 用于在目標方法調用后觸發;ThrowsAdvice 用于在目標方法拋出異常時觸發;MethodInterceptor 用于實現 Around 通知(Advice),在目方法執行的前后觸發。

            如果要實現相應功能,則需要實現上述對應的接口。例如:實現Before 通知(Advice)需要實現方法 void before(Method method, Object[] args, Object target) ,實現 After 通知(Advice) 需要實現方法 void afterReturning (Method method, Object[] args, Object target)。

            2、在Spring 中定義切入點

            在不能明確調用方法的時候,通知就很不實用。切入點則可以決定特定的類,特定的方法是否匹配特定標準。如果某匹配,則通知將應用到此方法上。Spring 切入點允許用很靈活的方式將通知組織進我們的類中。Spring 中的切入點框架的核心是Pointcut接口,此接口允許我們定義組入通知中的類和方法。許多方面是通過一系列的通知和切入點組合來定義。

            在Spring中,一個advisor就是一個方面的完整的模塊化表示。Spring提供了PointcutAdvisor接口把通知和切入點組合成一個對象。Spring中很多內建的切入點都有對應的PointcutAdvisor,這使得你可以很方便在一個地方管理切入點和通知。Spring中的切入點分為兩類:靜態和動態。因為靜態切入點的性能要優于動態切入點,所以優先考慮使用。Spring 為我們提供創建靜態切入點很實用的類StaticMethodMatherPointcut。在這個類中,我們只需要關心setMappedName和setMappedNams方法。你可以使用具體的類名,也可以使用通配符。如:設置mappedName屬性為set* 則匹配所有的set方法。Spring還提供了另通過正則表達式來創建靜態切入點的實用類RegexpMethodPointcut。通過使用Perl樣式的正則表達式來定義你感興趣的方法。當切入點需要運行時參數值來執行通知時,這時就需要使用動態切入點。Spring提供了一個內建的動態切入點:ControlFlowPointcut,此切入點匹配基于當前線程的調用堆棧。我們可以在只有在當前線程執行的執行時找到特定的類和特定的方法才返回true。使用動態切入點有很大的性能損耗。大多數的切入點可以靜態確定,我們很少有機會創建動態切入點。為了增加可切入點的可重用性,Spring 提供了切入點上的集合操作——交集和合并。

            3、用ProxyFactoryBean創建AOP代理

            ProxyFactoryBean,和其他Spring的 FactoryBean實現一樣,引入一個間接的層次。如果你定義一個名字為myfactory的ProxyFactoryBean, 引用myfactory的對象所看到的不是ProxyFactoryBean 實例本身,而是由實現ProxyFactoryBean的類的 getObject()方法所創建的對象。這個方法將創建一個包裝了目標對象 的AOP代理。使用ProxyFactoryBean或者其他IoC可知的類來創建AOP代理的最重要的優點之一是IoC可以管理通知和切入點。這是一個非常的強大的功能,能夠實現其他AOP框架很難實現的特定的方法。例如,一個通知本身可以引用應用對象(除了目標對象, 它在任何AOP框架中都可以引用應用對象),這完全得益于依賴注入所提供的可插入性。通常,我們不需要ProxyFactoryBean的全部功能,因為我們常常只對一個方面感興趣: 例如,事務管理。當我們僅僅對一個特定的方面感興趣時,我們可以使用許多便利的工廠來創建AOP代理,如:TransactionProxyFactoryBean。

            4、自動代理

            在應用較小時,只有很少類需要被通知的時,ProxyFactoryBean 可以很好的工作。當有許多類需要通知的時,顯示的創建每個代理就顯得很繁瑣。幸運的是Spring提供了是使用自動通過容器來創建代理。這時,就只需要配置一個Bean來做繁瑣的工作。Spring提供了兩個類實現自動代理:BeanNameAutoProxyCreator和DefaultAdvisorAutoProxyCreator。BeanNameAutoProxyCreator為匹配名字的Bean產生代理,它可以使用在將一個或者多個方面應用在命名相似的Bean中。自動代理框架假設代理將要暴露出什么接口。如果目標Bean沒有實現任何接口,這時就會動態產生一個子類。而更強大的自動代理是DefaultAdvisorAutoProxyCreator,你所需要做的是在BeanFactory中包含它的配置。這個類的奇妙之處在于他使用實現了BeanPostProcessor接口。在Bean定義被加載倒Spring容器中后,DefaultAdvisorAutoProxyCreator將搜索上下文中的Advisor,最后它將Advisor應用到匹配Advisor切入點的Bean中。這個代理只對Advisor起作用,它需要通過Advisor來得到需要通知的Bean。元數據自動代理(MetaData AutoProxy)。元數據自動代理配置依賴于源代碼屬性而不是外部XML配置文件。這可以非常方便的使源代碼和AOP元數據組織在同一個地方。元數據自動代理最常用的地方是用來聲明事務。Spring提供了很強的框架來通過AOP框架來聲明事務。這提供了在EJB使用聲明式事務的相同功能。

            結論

            AOP 是面向對象編程的有力補充。通過方面就可以聚合在應用中行為形成可重用模塊。

            通過程序可以實現怎樣和在什么地方來調用這些行為。這可以減少代碼重復,并使你更加關注業務邏輯。Spring 提供了AOP框架來實現調用方法時加入方面。在AOP框架中可以很方便的使用預定義的靜態切入點來定義被調用的類和方法。我們需要通過Spring提供的代理類來產生代理對象,可以使用ProxyFactoryBean也可以使用自動代理。Spring AOP 的編程方式模塊化了橫向關注點的實現,提供了一個更好更快的軟件開發方式。在軟件結構日益擴大,結構日益復雜的今天,Spring AOP 將會發揮越來越重要的作用。
          posted @ 2007-05-20 16:35 徐洪云 閱讀(187) | 評論 (0)編輯 收藏

          2007年5月14日 #

          使用struts+spring+hibernate組裝你的web應用架構

                其實,就算用Java建造一個不是很煩瑣的web應用,也不是件輕松的事情。 在構架的一開始就有很多事情要考慮。 從高處看,擺在開發者面前有很多問題:要考慮是怎樣建立用戶接口?在哪里處理業務邏輯? 怎樣持久化的數據。 而這三層構架中,每一層都有他們要仔細考慮的。 各個層該使用什么技術? 怎樣的設計能松散耦合還能靈活改變? 怎樣替換某個層而不影響整體構架?應用程序如何做各種級別的業務處理(比如事務處理)?

                構架一個Web應用需要弄明白好多問題。 幸運的是,已經有不少開發者已經遇到過這類問題,并且建立了處理這類問題的框架。 一個好框架具備以下幾點: 減輕開發者處理復雜的問題的負擔("不重復發明輪子"); 內部有良好的擴展; 并且有一個支持它的強大的用戶團體。 好的構架一般有針對性的處理某一類問題,并且能將它做好(Do One Thing well)。 然而,你的程序中有幾個層可能需要使用特定的框架,已經完成的UI(用戶接口) 并不代表你也可以把你的業務邏輯和持久邏輯偶合到你的UI部分。 舉個例子, 你不該在一個Controller(控制器)里面寫JDBC代碼作為你的業務邏輯, 這不是控制器應該提供的。 一個UI 控制器應該委派給其它給在UI范圍之外的輕量級組件。 好的框架應該能指導代碼如何分布。 更重要的是,框架能把開發者從編碼中解放出來,使他們能專心于應用程序的邏輯(這對客戶來說很重要)。

                這篇文章將討論怎樣結合幾種著名的框架來使得你的應用程序做到松弛耦合。
          如何建立你的架構,并且怎樣讓你的各個應用層保持一致。?如何整合框架以便讓每個層在以一種松散偶合的方式彼此作用而不用管低層的技術細節?這對我們來說真是一種挑戰。 這里討論一個整合框架的策略( 使用3 種受歡迎的開源框架) :表示層我們用Struts; 業務層我們用Spring;而持久層則用Hibernate。 你也可以用其他FrameWork替換只要能得到同樣的效果。 見圖1 (框架組合示意圖)



          應用程序的分層

          大部分的Web應用在職責上至少能被分成4層。 這四層是:presentation(描述),persistence(持久),business(業務)和domain model(域模塊)。每個層在處理程序上都應該有一項明確的責任, 而不應該在功能上與其它層混合,并且每個層要與其它層分開的,但要給他們之間放一個通信接口。 我們就從介紹各個層開始,討論一下這些層應該提供什么,不應該提供什么。



          表示層(The Presentation Layer)

          一般來講,一個典型的Web應用的的末端應該是表示層。 很多Java發者也理解Struts所提供的。 象業務邏輯之類的被打包到org.apache.struts.Action., 因此,我們很贊成使用Struts這樣的框架。



          下面是Struts所負責的:

          * 管理用戶的請求,做出相應的響應。

          * 提供一個Controller ,委派調用業務邏輯和其它上層處理。

          * 處理異常,拋給Struts Action

          * 為顯示提供一個模型

          * UI驗證。



          以下條款,不該在Struts顯示層的編碼中經常出現。 它們與顯示層無關的。

          * 直接的與數據庫通信,例如JDBC調用。

          * 與你應用程序相關聯的業務邏輯以及校驗。

          * 事物管理。

          在表示層引入這些代碼,則會帶來高偶合和麻煩的維護。





          持久層(The Persistence Layer)

          典型的Web應用的另一個末端是持久層。這里通常是程序最容易失控的地方。開發者總是低估構建他們自己的持久框架的挑戰性。系統內部的持續層不但需要大量調試時間,而且還經常缺少功能使之變得難以控制,這是持久層的通病。 還好有幾個ORM開源框架很好的解決了這類問題。尤其是Hibernate。 Hibernate為java提供了OR持久化機制和查詢服務, 它還給已經熟悉SQL和JDBC API 的Java開發者一個學習橋梁,他們學習起來很方便。 Hibernate的持久對象是基于POJO和Java collections。此外,使用Hibernate并不妨礙你正在使用的IDE。



          請看下面的條目,你在持久層編碼中需要了解的。

          * 查詢對象的相關信息的語句。 Hibernate通過一個OO查詢語言(HQL)或者正則表達的API來完成查詢。 HQL非常類似于SQL-- 只是把SQL里的table和columns用Object和它的fields代替。 你需要學習一些新的HQL語言; 不管怎樣,他們容易理解而文檔也做的很好。 HQL是一種對象查詢的自然語言,花很小的代價就能學習它。

          * 如何存儲,更新,刪除數據庫記錄。

          * 象Hibernate這類的高級ORM框架支持大部分主流數據庫,并且他們支持 Parent/child關系,事物處理,繼承和多態。



          業務層(The Business Layer)

          一個典型Web應用的中間部分是業務層或者服務層。 從編碼的視角來看,這層是最容易被忽視的一層。 而我們卻往往在UI層或持久層周圍看到這些業務處理的代碼,這其實是不正確的,因為它導致了程序代碼的緊密偶合,這樣一來,隨著時間推移這些代碼很難維護。幸好,針對這一問題有好幾種Frameworks存在。 最受歡迎的兩個框架是Spring和PicoContainer。 這些為也被稱為microcontainers,他們能讓你很好的把對象搭配起來。 這兩個框架都著手于‘依賴注射'(dependency injection)(還有我們知道的‘控制反轉'Inversion of Control=IoC)這樣的簡單概念。 這篇文章將關注于Spring的注射(譯注:通過一個給定參數的Setter方法來構造Bean,有所不同于Factory), Spring還提供了Setter Injection(type2),Constructor Injection(type3)等方式供我們選擇。 Spring把程序中所涉及到包含業務邏輯和Dao的Objects——例如transaction management handler(事物管理控制)、Object Factoris(對象工廠)、service objects(服務組件)——都通過XML來配置聯系起來。



          后面我們會舉個例子來揭示一下Spring 是怎樣運用這些概念。

          業務層所負責的如下:

          * 處理應用程序的 業務邏輯和業務校驗

          * 管理事物

          * 允許與其它層相互作用的接口

          * 管理業務層級別的對象的依賴。

          * 在顯示層和持久層之間增加了一個靈活的機制,使得他們不直接的聯系在一起。

          * 通過揭示 從顯示層到業務層之間的Context來得到business services。

          * 管理程序的執行(從業務層到持久層)。





          域模塊層(The Domain Model Layer )
          既然我們致力于的是一個不是很復雜的Web的應用, 我們需要一個對象集合,讓它在不同層之間移動的。 域模塊層由實際需求中的業務對象組成 比如, OrderLineItem , Product等等。 開發者在這層 不用管那些DTOs,僅關注domain object即可。 例如,Hibernate允許你將數據庫中的信息存放入對象(domain objects),這樣你可以在連接斷開的情況下把這些數據顯示到UI層。 而那些對象也可以返回給持續層,從而在數據庫里更新。 而且,你不必把對象轉化成DTOs(這可能似的它在不同層之間的在傳輸過程中丟失),這個模型使得Java開發者能很自然運用OO,而不需要附加的編碼。

          一個簡單例子



          既然我們已經從全局上理解這些組件。 現在就讓我們開始實踐吧。 我們還是用 Struts,Spring 和Hibernate。這三個框架已經被描述夠多了,這里就不重復介紹了。 這篇文章舉例指導你如何使用這三個框架整合開發, 并向你揭示 一個請求是如何貫穿于各個層的。(從用戶的加入一個Order到數據庫,顯示;進而更新、刪除)。



          從這里可以下載到程序程序原代碼(download

          既然每個層是互相作用的,我們就先來創建domain objects。首先,我們要在這些Object中要確定那些是需要持久化的,哪些是提供給business logic,那些是顯示接口的設計。 下一步,我們將配置我們的持久層并且定義好Hibernate的OR mappings。然后定義好Business Objects。有了這些組成部分之后,我們將 使用Spring把這些連接起來。 最后,我們提供給Spring一個持久層,從這個持久層里我們可以知道它是如何與業務邏輯層(business service layer)通信的,以及它是怎樣處理其他層拋出的異常的。。



          域對象層(Domain Object Layer)


          這層是編碼的著手點,我們的編碼就從這層開始。 例子中Order 與OrderItem 是一個One—To—Many的關系。 下面就是Domain Object Layer的兩個對象:



          · com.meagle.bo.Order.java: 包含了一個Order的概要信息

          · com.meagle.bo.OrderLineItem.java: 包含了Order的詳細信息

          好好考慮怎你的package命名,這反應出了你是怎樣分層的。 例如 domain objects在程序中可能打包在com.meagle.bo內。 更詳細一點將打包在com. meagle.bo的子目錄下面。business logic應該從com.meagle.serice開始打包,而DAO 對象應該位于com.meagle.service.dao.hibernate。反應Forms和Actions的 持久對象(presentation classes) 應該分別放在 com.meagle.action和com.meagle.forms包。 準確的給包命名使得你的classes很好分割并且易于維護,并且在你添加新的classes時,能使得程序結構上保持上下一致。

          持久層的配置(Persistence Layer Configuration)

          建立Hibernate的持久層 需要好幾個步驟。 第一步讓我們把BO持久化。 既然Hibernate是通過POJO工作的, 因此Order和 OrderLineItem對象需要給所有的fileds 加上getter,setter方法。 Hibernate通過XML文件來映射(OR)對象,以下兩個xml文件分別映射了Order 和OrderItem對象。(這里有個叫XDoclet工具可以自動生成你的XML影射文件)

          - Order.hbm.xml
          - OrderLineItem.hbm.xml

          你可以在WebContent/WEB-INF/classes/com/meagle/bo目錄下找到這些xml文件。Hibernate的 [urlhttp://www.hibernate.org/hib_docs/api/net/sf/hibernate/SessionFactory.html]SessionFactory [/url]是用來告訴程序 應該與哪個數據庫通信,該使用哪個連接池或使用了DataSource, 應該加載哪些持久對象。而Session接口是用來完成Selecting,Saving,Delete和Updating這些操作。 后面的我們將講述SessionFactory和Session是怎樣設置的。

          業務層的配置(Business Layer Configuration)

          既然我們已經有了domain objects,接下來我們就要business service objects了,用他們來執行程序的logic,調用持久層,得到UI層的requests,處理transactions,并且控制exceptions。 為了將這些連接起來并且易于管理,我們將使用面向方面的 SpringFramework。 Spring 提供了 控制倒置(inversion of control 0==IoC)和注射依賴設置(setter dependency injection)這些方式(可供選擇),用XML文件將對象連接起來。 IoC是一個簡單概念(它允許一個對象在上層接受其他對象的創建),用IoC這種方式讓你的對象從創建中釋放了出來,降低了偶合度。




          這里是一個沒有使用IoC的對象創建的例子,它有很高偶合度。




          圖 2.沒有使用 IoC. A 創建了 B 和 C

          而這里是一個使用IoC的例子,這種方式允許對象在高層可以創建并進入另外一個對象,所以這樣可以直接被執行。


          圖 3. 對象使用了 IoC。 A 包含了接受B,C的 setter方法 , 這同樣達到了 由A創建B,C的目的。

          建立我們的業務服務對象(Building Our Business Service Objects)


          Business Object中的Setter方法接受的是接口,這樣我們可以很松散的定義對象實現,然后注入。 在我們的案例中,我們將用一個business service object接收一個DAO,用它來控制domain objects的持久化。 由于在這個例子中使用了Hibernate,我們可以很方便的用其他持久框架實現 同時通知Spring 有新的DAO可以使用了。

          在面向接口的編程中,你會明白 "注射依賴"模式是怎樣松散耦合你的業務邏輯和持久機制的:)。



          下面是一個接口business service object,DAO代碼片段:


          代碼:

          public interface IOrderService {

            public abstract Order saveNewOrder(Order order)

              throws OrderException,

                     OrderMinimumAmountException;

           

            public abstract List findOrderByUser(

                                               String user)

                                     throws OrderException;

           

            public abstract Order findOrderById(int id)

                                     throws OrderException;

           

            public abstract void setOrderDAO(

                                       IOrderDAO orderDAO);

          }

           

          注意到這段代碼里有一個 setOrderDao(),它就是一個DAO Object設置方法(注射器)。 但這里并沒有一個getOrderDao的方法,這不必要,因為你并不會在外部訪問這個orderDao。這個DAO Objecte將被調用,和我們的persistence layer 通信。我們將用Spring把DAO Object 和 business service object搭配起來的。因為我們是面向接口編程的,所以并不需要將實現類緊密的耦合在一起。



          接下去我們開始我們的DAO的實現類進行編碼。 既然Spring已經有對Hibernate的支持,那這個例子就直接繼承HibernateDaoSupport類了,這個類很有用,我們可以參考HibernateTemplate(它主要是針對HibernateDaoSupport的一個用法,譯注:具體可以查看Srping 的API)。 下面是這個DAO接口代碼:

          代碼:
          public interface IOrderDAO {
            public abstract Order findOrderById(
                                              final int id);
           
            public abstract List findOrdersPlaceByUser(
                                     final String placedBy);
            public abstract Order saveOrder(
                                         final Order order);
          }


          我們仍然要給我們持久層組裝很多關聯的對象,這里包含了HibernateSessionFactory 和TransactionManager。 Spring 提供了一個 HibernateTransactionManager,他用線程捆綁了一個Hibernate Session,用它來支持transactions(請查看ThreadLocal) 。

          下面是HibernateSessionFactory 和 HibernateTransactionManager:的配置:

          代碼:
          <bean id="mySessionFactory"
                 class="org.springframework.orm.hibernate.
                        LocalSessionFactoryBean">
            <property name="mappingResources">
              <list>
                <value>
                  com/meagle/bo/Order.hbm.xml
                </value>
                <value>
                  com/meagle/bo/OrderLineItem.hbm.xml
                </value>
              </list>
            </property>
            <property name="hibernateProperties">
              <props>
                <prop key="hibernate.dialect">
                  net.sf.hibernate.dialect.MySQLDialect
                </prop>
                <prop key="hibernate.show_sql">
                  false
                </prop>
                <prop key="hibernate.proxool.xml">
                  C:/MyWebApps/.../WEB-INF/proxool.xml
                </prop>
                <prop key="hibernate.proxool.pool_alias">
                    spring
                </prop>
              </props>
            </property>
          </bean>
           
          <!-- Transaction manager for a single Hibernate
          SessionFactory (alternative to JTA) -->
          <bean id="myTransactionManager"
                   class="org.
                          springframework.
                          orm.
                          hibernate.
                          HibernateTransactionManager">
            <property name="sessionFactory">
              <ref local="mySessionFactory"/>
            </property>
            </bean>



          可以看出:每個對象都可以在Spring 配置信息中用<bean>標簽引用。在這里,mySessionFactory引用了HibernateSessionFactory,而myTransactionManager引用了HibernateTransactionManage。 注意代碼中myTransactionManger Bean有個sessionFactory屬性。 HibernateTransactionManager有個sessionFactory setter 和 getter方法,這是用來在Spring啟動的時候實現"依賴注入" (dependency injection)的。 在sessionFactory 屬性里 引用mySessionFactory。這兩個對象在Spring容器初始化后就被組裝了起來了。 這樣的搭配讓你從 單例(singleton objects)和工廠(factories)中解放了出來,降低了代碼的維護代價。 mySessionFactory.的兩個屬性,分別是用來注入mappingResources 和 hibernatePropertes的。通常,如果你在Spring之外使用Hibernate,這樣的設置應該放在hibernate.cfg.xml中的。 不管怎樣,Spring提供了一個便捷的方式-----在Spring內部配置中并入了Hibernate的配置。 如果要得到更多的信息,可以查閱Spring API。





          既然我們已經組裝配置好了Service Beans,就需要把Business Service Object和 DAO也組裝起來,并把這些對象配到一個事務管理器(transaction manager)里。



          在Spring中的配置信息:
          代碼:

          <!-- ORDER SERVICE -->
          <bean id="orderService"
            class="org.
                   springframework.
                   transaction.
                   interceptor.
                   TransactionProxyFactoryBean">
            <property name="transactionManager">
              <ref local="myTransactionManager"/>
            </property>
            <property name="target">
              <ref local="orderTarget"/>
            </property>
            <property name="transactionAttributes">
              <props>
                <prop key="find*">
               PROPAGATION_REQUIRED,readOnly,-OrderException
                </prop>
                <prop key="save*">
               PROPAGATION_REQUIRED,-OrderException
                </prop>
              </props>
            </property>
          </bean>
           
          <!-- ORDER TARGET PRIMARY BUSINESS OBJECT:
          Hibernate implementation -->
          <bean id="orderTarget"
                   class="com.
                          meagle.
                          service.
                          spring.
                          OrderServiceSpringImpl">
            <property name="orderDAO">
              <ref local="orderDAO"/>
            </property>
          </bean>
           
          <!-- ORDER DAO OBJECT -->
          <bean id="orderDAO"
                   class="com.
                          meagle.
                          service.
                          dao.
                          hibernate.
                          OrderHibernateDAO">
            <property name="sessionFactory">
              <ref local="mySessionFactory"/>
            </property>
          </bean>




          圖4 是我們對象搭建的一個提綱。 從中可以看出,每個對象都聯系著Spring,并且能通過Spring注入到其他對象。把它與Spring的配置文件比較,觀察他們之間的關系



          圖 4. Spring就是這樣基于配置文件,將各個Bean搭建在一起。

          這個例子使用一個TransactionProxyFactoryBean,它定義了一個setTransactionManager()。 這對象很有用,他能很方便的處理你申明的事物還有Service Object。 你可以通過transactionAttributes屬性來定義怎樣處理。 想知道更多還是參考TransactionAttributeEditor吧。

          TransactionProxyFactoryBean 還有個setter. 這會被我們 Business service object(orderTarget)引用, orderTarget定義了 業務服務層,并且它還有個屬性,由setOrderDAO()引用。這個屬性



          Spring 和Bean 的還有一點要注意的: bean可以以用兩種方式創造。 這些都在單例模式(Sington)和原型模式(propotype)中定義了。 默認的方式是singleton,這意味著共享的實例將被束縛。 而原形模式是在Spring用到bean的時候允許新建實例的。當每個用戶需要得到他們自己Bean的Copy時,你應該僅使用prototype模式。(更多的請參考設計模式中的單例模式和原形模式)

          提供一個服務定位器(Providing a Service Locator)
          既然我們已經將我們的Serices和DAO搭配起來了。我們需要把我們的Service顯示到其他層。 這個通常是在Struts或者Swing這層里編碼。一個簡單方法就是用 服務定位器返回給Spring context 。當然,可以通過直接調用Spring中的Bean來做。

          下面是一個Struts Actin 中的服務定位器的一個例子。
          代碼:

          public abstract class BaseAction extends Action {
           
            private IOrderService orderService;
           
            public void setServlet(ActionServlet
                                           actionServlet) {
              super.setServlet(actionServlet);
              ServletContext servletContext =
                         actionServlet.getServletContext();
           
              WebApplicationContext wac =
                WebApplicationContextUtils.
                   getRequiredWebApplicationContext(
                                           servletContext);
           
                this.orderService = (IOrderService)
                               wac.getBean("orderService");
            }
           
            protected IOrderService getOrderService() {
              return orderService;
            }
          }
           

          UI 層配置 (UI Layer Configuration)

          這個例子里UI層 使用了Struts framework. 這里我們要講述一下在給程序分層的時候, 哪些是和Struts部分的。我們就從一個Struts-config.xml文件中的Action的配置信息開始吧。
          代碼:

          struts-config.xml file.

          <action path="/SaveNewOrder"
              type="com.meagle.action.SaveOrderAction"
              name="OrderForm"
              scope="request"
              validate="true"
              input="/NewOrder.jsp">
            <display-name>Save New Order</display-name>
            <exception key="error.order.save"
              path="/NewOrder.jsp"
              scope="request"
              type="com.meagle.exception.OrderException"/>
            <exception key="error.order.not.enough.money"
              path="/NewOrder.jsp"
              scope="request"
              type="com.
                    meagle.
                    exception.
                    OrderMinimumAmountException"/>
            <forward name="success" path="/ViewOrder.jsp"/>
            <forward name="failure" path="/NewOrder.jsp"/>
          </action>

          SaveNewOrder 這個Action是用來持久化UI層里的表單提交過來Order的。這是Struts中一個很典型的Action; 注意觀察這個Action中exception配置,這些Exceptions也在Spring 配置文件(applicationContext-hibernate.xml)中配置了(就在 business service object 的transactionAttributes屬性里)。 當異常在業務層被被拋出時,我們可以控制他們,并適當的顯示給UI層。

          第一個異常,OrderException,在持久層保存order對象失敗的時候被觸發。這將導致事物回滾并且通過BO把異常回傳到Struts這一層。

          第二個異常,OrderMinimumAmountException也同第一個一樣。





          搭配整和的最后一步 通過是讓你顯示層和業務層相結合。這個已經被服務定位器(service locator)實現了(前面討論過了), 這里服務層作為一個接口提供給我們的業務邏輯和持久層。



          SaveNewOrder Action 在Struts中用一個服務定位器(service locator)來調用執行業務方法的。 方法代碼如下:



          代碼:
          public ActionForward execute(

            ActionMapping mapping,

            ActionForm form,

            javax.servlet.http.HttpServletRequest request,

            javax.servlet.http.HttpServletResponse response)

            throws java.lang.Exception {

           

            OrderForm oForm = (OrderForm) form;

           

            // Use the form to build an Order object that

            // can be saved in the persistence layer.

            // See the full source code in the sample app.

           

            // Obtain the wired business service object

            // from the service locator configuration

            // in BaseAction.

            // Delegate the save to the service layer and

            // further upstream to save the Order object.

            getOrderService().saveNewOrder(order);

           

            oForm.setOrder(order);

           

            ActionMessages messages = new ActionMessages();

            messages.add(

                ActionMessages.GLOBAL_MESSAGE,

                      new ActionMessage(

                "message.order.saved.successfully"));

           

            saveMessages(request, messages);

           

            return mapping.findForward("success");

          }


          總結

          這篇文章在技術和構架方面掩蓋了很多低層的基礎信息, 文章的主要的意圖在于讓你意識到如何給你應用程序分層。 分層可以"解耦"你的代碼——允許新的組件被添加進來,而且讓你的代碼易于維護。 這里用到的技術只是專注于把"解偶"做好。 不管怎樣,使用這樣的構架可以讓你用其他技術代替現在的層。 例如,你可能不使用Hibernate實現持久化。既然你在DAO中面向接口的編程的,所以你完全可以用iBATIS來代替。或者,你也可能想用Struts外的其他的技術或者框架替換現在的UI層(轉換久層,實現層并不應該直接影響到你的業務邏輯和業務服務層)。 用適當的框架搭建你的Web應用,其實也不是一件煩瑣的工作,更主要的是它"解耦"了你程序中的各個層。





          后記:

          看完這篇文章后,只是覺得很喜歡,于是就翻譯了,當然同時也準備著挨大家扔來的雞蛋:)。

          這篇文章里并沒有太多的技術細節,和詳細的步驟。如果你從未使用過這些框架而在運行實例程序遇上困難的話,可以到CSDN論壇Java Open Source版發貼,我一定會詳細解答的(啊哦,這不算做廣告吧?),

          文章是從一個構架的角度講述了如何搭配現有的開源框架進行分層, 有太多的術語我都不知道怎么表達,而且可能有很多語句存在錯誤。如果影響了你的閱讀,請你直接點原文地址,我同時也象你說聲抱歉。



          作者簡介:Mark Eagle 高級軟件工程師,亞特蘭大。
          翻 譯:Totodo,軟件工程師





          參考:

          Struts:http://jakarta.apache.org/struts/index.html

          Spring: http://www.springframework.org

          Hibernate: http://www.hibernate.org

          http://www.hibernate.org.cn

          關于控制反轉IOC和依賴注射:http://www.martinfowler.com/articles/injection.html

          原文:

          http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=1
          http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=2
          http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=3
          http://www.onjava.com/pub/a/onjava/2004/04/07/wiringwebapps.html?page=4
          posted @ 2007-05-14 04:32 徐洪云 閱讀(202) | 評論 (1)編輯 收藏

          僅列出標題  
          主站蜘蛛池模板: 汾西县| 沽源县| 萝北县| 古浪县| 淮滨县| 罗甸县| 洛阳市| 惠东县| 苏尼特左旗| 西丰县| 南木林县| 聊城市| 台江县| 库车县| 邢台市| 南安市| 灵宝市| 揭西县| 云龙县| 浦县| 远安县| 犍为县| 五常市| 白河县| 黄石市| 方山县| 永靖县| 古丈县| 鹰潭市| 巴青县| 抚松县| 福泉市| 垣曲县| 盐边县| 海淀区| 三门县| 冷水江市| 千阳县| 屏东市| 平陆县| 土默特右旗|