抽象出一個數據訪問的API是不夠的;我們還需要考慮事務管理。JTA是顯而易見的選擇,但是它是一個直接用起來很笨重的API,因而許多J2EE開發者感到EJB CMT是對于事務管理唯一合理的選擇。
Spring提供了它自己對事務管理的抽象。Spring提供了這些:
通過類似于JdbcTemplate的回調模板編程管理事務,比起直接使用JTA要容易多了
類似于EJB CMT的聲明式事務管理,但是不需要EJB容器
Spring的事務抽象式唯一的,它不綁定到JTA或者任何其他事務管理技術。Spring使用事務策略的概念把程序代碼和底層的事務架構(例如JDBC)解藕。
為什么你要關心這些?JTA不是所有事務管理的最好答案嗎?如果你正在編寫僅僅使用一個數據庫的程序,你不需要JTA的復雜度。你不關心XA事務或者兩階段提交。你甚至不需要提供這些東西的高端應用服務器。但是另一方面,你不會希望在需要和多個數據源打交道的時候重寫你的代碼。
假定你決定通過直接使用JDBC或者Hibernate的事務以避免JTA帶來的額外負擔。一旦你需要處理多個數據源,你必須剝開所有的事務管理代碼并且使用JTA事務來替代。這不是非常有吸引力的并且導致大部分J2EE程序員,包括我自己,推薦只使用全局JTA事務。然而使用Spring事務抽象,你只需要重新配置Spring讓它使用JTA,而不是JDBC或者Hibernate的事務策略,就一切OK了。這是一個配置上的改變,而不是代碼的改動。因而,Spring使得你能夠自由縮放應用。
AOP
最近在應用AOP來解決企業關注點方面大家有了很大的興趣,例如事務管理,這些都是EJB所要解決的。
Spring的AOP支持的首要目標是要給POJOs提供J2EE服務。這類似于JBoss 4的目標,Spring AOP由它能夠在應用服務器之間移植的優勢,因而沒有綁死在廠商身上的風險。它既可以在web或者EJB容器中使用,也能夠在WebLogic,Tomcat,JBoss,Resin,Jetty,Orion和許多其他應用服務器和web容器上使用。
Spring AOP支持method interception。所支持關鍵的AOP概念包括:
Interception:自定義行為能夠在對接口和類的調用之前和之后插入。這類似于AspectJ術語中類似的“around advice”。
Introduction:指定advice會導致對象實現額外的接口。這混亂了繼承。
靜態和動態的pointcuts:在interception發生的程序執行處指定points。靜態pointcuts concern函數簽名;動態pointcuts也可以在point被求值的地方考慮函數的參數。Pointcuts獨立interceptors單獨定義,使得標準interceptor可以應用于不同應用程序和代碼上下文。
Spring既支持有狀態(一個advised對象一個實例)也支持無狀態的interceptors(所有advice使用一個實例)。
Spring不支持field interception。這是一個經過深思熟慮的設計決定。我總是感覺field interception違反了封裝。我比較傾向于把AOP作為補全物,而不是與OOP沖突的東西。如果在5年或者10年后,我們在AOP學習曲線上走得更遠了并且覺得應該在程序設計的桌面上給AOP一個位置,我不會驚訝的。(然而在那個時候基于語言的解決方案例如AspectJ可能比它們今天看來更加具有吸引力。)
Spring使用動態代理實現AOP(其中存在一個接口)或者在運行時使用CGLIB生成字節碼(這使得能夠代理類)。兩種方法都能夠在任何應用服務器中使用。
Spring是第一個實現AOP Alliance interfaces的AOP 框架(www.sourceforge.net/projects/aopalliance)。這些是定義在不同AOP框架中能夠互操作interceptors的嘗試。
在TheServerSide和其他地方有一個正在進行但是不是那么引人注目的爭論,就是這種interception是不是“true AOP”。我倒不怎么在意它叫什么;僅僅需要知道它是否在實踐中有用就好了。我也樂于稱它為“declarative middleware”(聲明式中間件)。把Spring AOP認做簡單,輕量級的無狀態beans的替代物,這樣就不需要monolithic EJB容器了,而這些僅僅是讓你能夠構建有你需要的服務的容器。我不推薦advising任何一個POJO,對local SLSBs的類比有助于你理解推薦的粒度。(然而,與EJB不同的是,在恰當但是少見的情況下,你可以自由地把Spring的AOP應用到粒度更好的對象上。)
因為Spring在實例上advises 對象,而不是在class loader層面上,使用有不同advice的同一個類的多個實例是可能的,或者與advised實例一道使用unadvised 實例。
可能Spring AOP最常見的應用是聲明式事務管理。這是基于前面描述的TansactionTemplate抽象上的,并且可以給任何POJO提供聲明式事務管理。取決于事務策略,底層的機制可以是JTA,JDBC,Hibernate或者任何其他提供事務管理的API。
Spring的聲明式事務管理類似于EJB CMT,在以下方面有些不同:
事務管理能夠應用于任何POJO。我們推薦業務對象實現接口,但是這只是一個好的編程習慣的問題,而不是由框架強制的。
通過使用Spring的事務API能夠在事務性POJO中實現編程回調。我們為此提供靜態的方法,使用ThreadLoacal變量,因而你不需要傳播諸如EJBContext這樣的context對象來確?;貪L。
你可以聲明式地定義“回滾規則”。EJB不會在未捕捉程序異常的時候自動回滾(僅僅在unchecked exceptions和其他Throwables的時候),應用程序開發者經常需要在任何異常發生時回滾。Spring事務管理讓你能夠聲明式地指定什么異常什么子類能夠導致自動回滾。缺省的行為和EJB是一致的,但是你能夠在checked和unchecked異常時自動回滾。這個在最少化自編程回調代碼方面有很大好處,而回調依賴于Spring的事務API(因為EJB的編程回調時在EJBContext中完成的)。
事務管理不綁定于JTA。如前面解釋過的,Spring的事務管理能夠在不同事務策略中使用。
當然還可以使用Spring AOP實現程序特有的aspects。取決于你對AOP概念的接受程度,決定你是否選擇這么做,而不是Spring的能力,但是它確實非常有用。我們所見過的成功例子包括:
自定義的security interception,當安全檢查的復雜度超出了J2EE安全架構的能力的時候
在開發中使用的調試和profiling aspects
發送email通知管理員用戶不尋常的舉動的Interceptors
程序自定的aspects能夠成為消除需要許多函數的樣板代碼的有利武器。
Spring AOP透明地與Spring BeanFactory概念集成。包含一個來自Spring BeanFactory對象地代碼不需要知道它是還是不是advised。和任何對象一樣,契約實在接口和對象實現中定義的。
下面的XML片斷展示了如何定義一個AOP代理:
代碼: |
<bean id="myTest" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>org.springframework.beans.ITestBean</value> </property> <property name="interceptorNames"> <list> <value>txInterceptor</value> <value>target</value> </list> </property> </bean> |
注意bean類的定義總是AOP框架的ProxyFactoryBean,雖然bean的類型在引用中使用或者由BeanFactory的getBean()方法返回時依賴的是代理接口。(多個代理方法是被支持的。)ProxyFactoryBean的“interceptorNames”屬性需要一個字符串列表。(因為如果代理是一個“prototype”而不是singleton,有狀態interceptors可能需要創建新的實例,所以必須使用Bean的名字而不是bean的引用。)列表中的名字可以是interceptor或者pointcuts(interceptors和有關它們合適被使用的信息)。列表中的“target”值自動創建一個“invoker interceptor”封裝target對象。實現代理接口的是在factory中的bean的名字。這個例子中的myTest可以和其他bean factory中的bean一樣使用。例如,其他對象可以使用<ref>元素引用它而且這些引用是由Spring IoC設置的。
還可以不用BeanFactory,編程構建AOP代理,雖然這個很少用得上:
代碼: |
TestBean target = new TestBean(); DebugInterceptor di = new DebugInterceptor(); MyInterceptor mi = new MyInterceptor(); ProxyFactory factory = new ProxyFactory(target); factory.addInterceptor(0, di); factory.addInterceptor(1, mi); // An "invoker interceptor" is automatically added to wrap the target ITestBean tb = (ITestBean) factory.getProxy(); |
我們相信最好把程序裝配從Java代碼中移出來,而AOP也不例外。
Spring在它的AOP能力方面的直接競爭者是Jon Tirsen的Nanning Aspects(http://nanning.codehaus.org)。
我覺得AOP作為EJB的替代無提供企業服務這個用法方面的進步是重要的。隨著時間,這將成為Spring很重要的關注點。