Spring - Java/J2EE Application Framework 应用框架 第 5 章 Spring AOP: Spring之面向方面编程
第?5?章?Spring AOP: Spring之面向方面編程
5.1.?概念
面向方面編程?(AOP) 提供從另一個(gè)角度來(lái)考慮程序結(jié)構(gòu)以完善面向?qū)ο缶幊?#xff08;OOP)。 面向?qū)ο髮?yīng)用程序分解成 各個(gè)層次的對(duì)象,而AOP將程序分解成各個(gè)方面?或者說(shuō)?關(guān)注點(diǎn)?。 這使得可以模塊化諸如事務(wù)管理等這些橫切多個(gè)對(duì)象的關(guān)注點(diǎn)。(這些關(guān)注點(diǎn)術(shù)語(yǔ)稱(chēng)作橫切關(guān)注點(diǎn)。)
Spring的一個(gè)關(guān)鍵組件就是AOP框架。 Spring IoC容器(BeanFactory 和ApplicationContext)并不依賴(lài)于AOP, 這意味著如果你不需要使用,AOP你可以不用,AOP完善了Spring IoC,使之成為一個(gè)有效的中間件解決方案,。
AOP在Spring中的使用:
-
提供聲明式企業(yè)服務(wù),特別是作為EJB聲明式服務(wù)的替代品。這些服務(wù)中最重要的是?聲明式事務(wù)管理,這個(gè)服務(wù)建立在Spring的事務(wù)管理抽象之上。
-
允許用戶(hù)實(shí)現(xiàn)自定義的方面,用AOP完善他們的OOP的使用。
這樣你可以把Spring AOP看作是對(duì)Spring的補(bǔ)充,它使得Spring不需要EJB就能提供聲明式事務(wù)管理;或者 使用Spring AOP框架的全部功能來(lái)實(shí)現(xiàn)自定義的方面。
如果你只使用通用的聲明式服務(wù)或者預(yù)先打包的聲明式中間件服務(wù)如pooling,你可以不直接使用 Spring AOP,并且跳過(guò)本章的大部分內(nèi)容.5.1.1.?AOP概念
讓我們從定義一些重要的AOP概念開(kāi)始。這些術(shù)語(yǔ)不是Spring特有的。不幸的是,Spring的術(shù)語(yǔ) 不是特別地直觀(guān)。而且,如果Spring使用自己的術(shù)語(yǔ),這將使人更加迷惑。
-
方面(Aspect): 一個(gè)關(guān)注點(diǎn)的模塊化,這個(gè)關(guān)注點(diǎn)實(shí)現(xiàn)可能 另外橫切多個(gè)對(duì)象。事務(wù)管理是J2EE應(yīng)用中一個(gè)很好的橫切關(guān)注點(diǎn)例子。方面用Spring的 Advisor或攔截器實(shí)現(xiàn)。
-
連接點(diǎn)(Joinpoint): 程序執(zhí)行過(guò)程中明確的點(diǎn),如方法的調(diào) 用或特定的異常被拋出。
-
通知(Advice): 在特定的連接點(diǎn),AOP框架執(zhí)行的動(dòng)作。各種類(lèi) 型的通知包括“around”、“before”和“throws”通知。通知類(lèi)型將在下面討論。許多AOP框架 包括Spring都是以攔截器做通知模型,維護(hù)一個(gè)“圍繞”連接點(diǎn)的攔截器 鏈。
-
切入點(diǎn)(Pointcut): 指定一個(gè)通知將被引發(fā)的一系列連接點(diǎn) 的集合。AOP框架必須允許開(kāi)發(fā)者指定切入點(diǎn):例如,使用正則表達(dá)式。
-
引入(Introduction): 添加方法或字段到被通知的類(lèi)。 Spring允許引入新的接口到任何被通知的對(duì)象。例如,你可以使用一個(gè)引入使任何對(duì)象實(shí)現(xiàn)?IsModified接口,來(lái)簡(jiǎn)化緩存。
-
目標(biāo)對(duì)象(Target Object): 包含連接點(diǎn)的對(duì)象。也被稱(chēng)作?被通知或被代理對(duì)象。
-
AOP代理(AOP Proxy): AOP框架創(chuàng)建的對(duì)象,包含通知。 在Spring中,AOP代理可以是JDK動(dòng)態(tài)代理或者CGLIB代理。
-
織入(Weaving): 組裝方面來(lái)創(chuàng)建一個(gè)被通知對(duì)象。這可以在編譯時(shí) 完成(例如使用AspectJ編譯器),也可以在運(yùn)行時(shí)完成。Spring和其他純Java AOP框架一樣, 在運(yùn)行時(shí)完成織入。
各種通知類(lèi)型包括:
-
Around通知: 包圍一個(gè)連接點(diǎn)的通知,如方法調(diào)用。這是最 強(qiáng)大的通知。Aroud通知在方法調(diào)用前后完成自定義的行為。它們負(fù)責(zé)選擇繼續(xù)執(zhí)行連接點(diǎn)或通過(guò) 返回它們自己的返回值或拋出異常來(lái)短路執(zhí)行。
-
Before通知: 在一個(gè)連接點(diǎn)之前執(zhí)行的通知,但這個(gè)通知 不能阻止連接點(diǎn)前的執(zhí)行(除非它拋出一個(gè)異常)。
-
Throws通知: 在方法拋出異常時(shí)執(zhí)行的通知。Spring提供 強(qiáng)類(lèi)型的Throws通知,因此你可以書(shū)寫(xiě)代碼捕獲感興趣的異常(和它的子類(lèi)),不需要從Throwable 或Exception強(qiáng)制類(lèi)型轉(zhuǎn)換。
-
After returning通知: 在連接點(diǎn)正常完成后執(zhí)行的通知, 例如,一個(gè)方法正常返回,沒(méi)有拋出異常。
Around通知是最通用的通知類(lèi)型。大部分基于攔截的AOP框架,如Nanning和JBoss4,只提供 Around通知。
如同AspectJ,Spring提供所有類(lèi)型的通知,我們推薦你使用最為合適的通知類(lèi)型來(lái)實(shí)現(xiàn)需 要的行為。例如,如果只是需要用一個(gè)方法的返回值來(lái)更新緩存,你最好實(shí)現(xiàn)一個(gè)after returning 通知而不是around通知,雖然around通知也能完成同樣的事情。使用最合適的通知類(lèi)型使編程模型變 得簡(jiǎn)單,并能減少潛在錯(cuò)誤。例如你不需要調(diào)用在around通知中所需使用的的MethodInvocation的?proceed()方法,因此就調(diào)用失敗。
切入點(diǎn)的概念是AOP的關(guān)鍵,使AOP區(qū)別于其它使用攔截的技術(shù)。切入點(diǎn)使通知獨(dú)立于OO的 層次選定目標(biāo)。例如,提供聲明式事務(wù)管理的around通知可以被應(yīng)用到跨越多個(gè)對(duì)象的一組方法上。 因此切入點(diǎn)構(gòu)成了AOP的結(jié)構(gòu)要素。
5.1.2.?Spring AOP的功能
Spring AOP用純Java實(shí)現(xiàn)。它不需要特別的編譯過(guò)程。Spring AOP不需要控制類(lèi)裝載器層次, 因此適用于J2EE web容器或應(yīng)用服務(wù)器。
Spring目前支持?jǐn)r截方法調(diào)用。成員變量攔截器沒(méi)有實(shí)現(xiàn),雖然加入成員變量攔截器支持并不破壞 Spring AOP核心API。
成員變量攔截器在違反OO封裝原則方面存在爭(zhēng)論。我們不認(rèn)為這在應(yīng)用程序開(kāi)發(fā)中是明智的。如 果你需要使用成員變量攔截器,考慮使用AspectJ。Spring提供代表切入點(diǎn)或各種通知類(lèi)型的類(lèi)。Spring使用術(shù)語(yǔ)advisor來(lái) 表示代表方面的對(duì)象,它包含一個(gè)通知和一個(gè)指定特定連接點(diǎn)的切入點(diǎn)。
各種通知類(lèi)型有MethodInterceptor?(來(lái)自AOP聯(lián)盟的攔截器API)和定義在org.springframework.aop包中的 通知接口。所有通知必須實(shí)現(xiàn)org.aopalliance.aop.Advice標(biāo)簽接口。 取出就可使用的通知有?MethodInterceptor、?ThrowsAdvice、?BeforeAdvice和?AfterReturningAdvice。我們將在下面詳細(xì)討論這些通知類(lèi)型。
Spring實(shí)現(xiàn)了AOP聯(lián)盟的攔截器接口(?http://www.sourceforge.net/projects/aopalliance). Around通知必須實(shí)現(xiàn)AOP聯(lián)盟的org.aopalliance.intercept.MethodInterceptor?接口。這個(gè)接口的實(shí)現(xiàn)可以運(yùn)行在Spring或其他AOP聯(lián)盟兼容的實(shí)現(xiàn)中。目前JAC實(shí)現(xiàn)了AOP聯(lián)盟的接 口,Nanning和Dynaop可能在2004年早期實(shí)現(xiàn)。
Spring實(shí)現(xiàn)AOP的途徑不同于其他大部分AOP框架。它的目標(biāo)不是提供及其完善的AOP實(shí)現(xiàn)( 雖然Spring AOP非常強(qiáng)大);而是提供一個(gè)和Spring IoC緊密整合的AOP實(shí)現(xiàn),幫助解決企業(yè)應(yīng)用 中的常見(jiàn)問(wèn)題。因此,例如Spring AOP的功能通常是和Spring IoC容器聯(lián)合使用的。AOP通知是用普通 的bean定義語(yǔ)法來(lái)定義的(雖然可以使用"autoproxying"功能);通知和切入點(diǎn)本身由Spring IoC 管理:這是一個(gè)重要的其他AOP實(shí)現(xiàn)的區(qū)別。有些事使用Spring AOP是無(wú)法容易或高效地實(shí)現(xiàn),比如通知 非常細(xì)粒度的對(duì)象。這種情況AspectJ可能是最合適的選擇。但是,我們的經(jīng)驗(yàn)是Spring針對(duì)J2EE應(yīng) 用中大部分能用AOP解決的問(wèn)題提供了一個(gè)優(yōu)秀的解決方案。5.1.3.?Spring中AOP代理
Spring默認(rèn)使用JDK動(dòng)態(tài)代理實(shí)現(xiàn)AOP代理。這使得任何接口或 接口的集合能夠被代理。
Spring也可以是CGLIB代理。這可以代理類(lèi),而不是接口。如果業(yè)務(wù)對(duì)象沒(méi)有實(shí)現(xiàn)一個(gè)接口, CGLIB被默認(rèn)使用。但是作為一針對(duì)接口編程而不是類(lèi)編程良好實(shí)踐,業(yè)務(wù)對(duì)象 通常實(shí)現(xiàn)一個(gè)或多個(gè)業(yè)務(wù)接口。
也可以強(qiáng)制使用CGLIB:我們將在下面討論,并且會(huì)解釋為什么你會(huì)要這么做。
Spring 1.0后,Spring可能提供額外的AOP代理的類(lèi)型,包括完全生成的類(lèi)。這將不會(huì)影響 編程模型。5.2.?Spring的切入點(diǎn)
讓我們看看Spring如何處理切入點(diǎn)這個(gè)重要的概念。
5.2.1.?概念
Spring的切入點(diǎn)模型能夠使切入點(diǎn)獨(dú)立于通知類(lèi)型被重用。 同樣的切入點(diǎn)有可能接受不同的 通知。
org.springframework.aop.Pointcut?接口是重要的接口, 用來(lái)指定通知到特定的類(lèi)和方法目標(biāo)。完整的接口定義如下:
public interface Pointcut {ClassFilter getClassFilter();MethodMatcher getMethodMatcher();}將Pointcut接口分成兩個(gè)部分有利于重用類(lèi)和方法的匹配部分,并且組合細(xì)粒度的 操作(如和另一個(gè)方法匹配器執(zhí)行一個(gè)”并“的操作)。
ClassFilter接口被用來(lái)將切入點(diǎn)限制到一個(gè)給定的目標(biāo)類(lèi)的集合。 如果matches()永遠(yuǎn)返回true,所有的目標(biāo)類(lèi)都將被匹配。
public interface ClassFilter {boolean matches(Class clazz); }MethodMatcher接口通常更加重要。完整的接口如下:
public interface MethodMatcher {boolean matches(Method m, Class targetClass);boolean isRuntime();boolean matches(Method m, Class targetClass, Object[] args); }matches(Method, Class)?方法被用來(lái)測(cè)試這個(gè)切入點(diǎn)是否匹 配目標(biāo)類(lèi)的給定方法。這個(gè)測(cè)試可以在AOP代理創(chuàng)建的時(shí)候執(zhí)行,避免在所有方法調(diào)用時(shí)都需要進(jìn)行 測(cè)試。如果2個(gè)參數(shù)的匹配方法對(duì)某個(gè)方法返回true,并且MethodMatcher的?isRuntime()也返回true,那么3個(gè)參數(shù)的匹配方法將在每次方法調(diào)用的時(shí)候被調(diào)用。這使 切入點(diǎn)能夠在目標(biāo)通知被執(zhí)行之前立即查看傳遞給方法調(diào)用的參數(shù)。
大部分MethodMatcher都是靜態(tài)的,意味著isRuntime()方法 返回false。這種情況下3個(gè)參數(shù)的匹配方法永遠(yuǎn)不會(huì)被調(diào)用。
如果可能,盡量使切入點(diǎn)是靜態(tài)的,使當(dāng)AOP代理被創(chuàng)建時(shí),AOP框架能夠緩存切入點(diǎn)的 測(cè)試結(jié)果。5.2.2.?切入點(diǎn)的運(yùn)算
Spring支持的切入點(diǎn)的運(yùn)算有: 值得注意的是?并?和?交。
并表示只要任何一個(gè)切入點(diǎn)匹配的方法。
交表示兩個(gè)切入點(diǎn)都要匹配的方法。
并通常比較有用。
切入點(diǎn)可以用org.springframework.aop.support.Pointcuts?類(lèi)的靜態(tài)方法來(lái)組合,或者使用同一個(gè)包中的ComposablePointcut類(lèi)。
5.2.3.?實(shí)用切入點(diǎn)實(shí)現(xiàn)
Spring提供幾個(gè)實(shí)用的切入點(diǎn)實(shí)現(xiàn)。一些可以直接使用。另一些需要子類(lèi)化來(lái)實(shí)現(xiàn)應(yīng)用相 關(guān)的切入點(diǎn)。
5.2.3.1.?靜態(tài)切入點(diǎn)
靜態(tài)切入點(diǎn)只基于方法和目標(biāo)類(lèi),而不考慮方法的參數(shù)。靜態(tài)切入點(diǎn)足夠滿(mǎn)足大多數(shù)情況 的使用。Spring可以只在方法第一次被調(diào)用的時(shí)候計(jì)算靜態(tài)切入點(diǎn),不需要在每次方法調(diào)用 的時(shí)候計(jì)算。
讓我們看一下Spring提供的一些靜態(tài)切入點(diǎn)的實(shí)現(xiàn)。
5.2.3.1.1.?正則表達(dá)式切入點(diǎn)
一個(gè)很顯然的指定靜態(tài)切入點(diǎn)的方法是正則表達(dá)式。除了Spring以外,其它的AOP框架也實(shí) 現(xiàn)了這一點(diǎn)。org.springframework.aop.support.RegexpMethodPointcut?是一個(gè)通用的正則表達(dá)式切入點(diǎn),它使用Perl 5的正則表達(dá)式的語(yǔ)法。
使用這個(gè)類(lèi)你可以定義一個(gè)模式的列表。如果任何一個(gè)匹配,那個(gè)切入點(diǎn)將被計(jì)算成 true。(所以結(jié)果相當(dāng)于是這些切入點(diǎn)的并集)。
用法如下:
<bean id="settersAndAbsquatulatePointcut" class="org.springframework.aop.support.RegexpMethodPointcut"><property name="patterns"><list><value>.*get.*</value><value>.*absquatulate</value></list></property> </bean>RegexpMethodPointcut一個(gè)實(shí)用子類(lèi),?RegexpMethodPointcutAdvisor, 允許我們同時(shí)引用一個(gè)通知。 (記住通知可以是攔截器,before通知,throws通知等等。)這簡(jiǎn)化了bean的裝配,因?yàn)橐粋€(gè)bean 可以同時(shí)當(dāng)作切入點(diǎn)和通知,如下所示:
<bean id="settersAndAbsquatulateAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"><property name="interceptor"><ref local="beanNameOfAopAllianceInterceptor"/></property><property name="patterns"><list><value>.*get.*</value><value>.*absquatulate</value></list></property> </bean>RegexpMethodPointcutAdvisor可以用于任何通知類(lèi)型。
RegexpMethodPointcut類(lèi)需要Jakarta ORO正則表達(dá)式包。5.2.3.1.2.?屬性驅(qū)動(dòng)的切入點(diǎn)
一類(lèi)重要的靜態(tài)切入點(diǎn)是元數(shù)據(jù)驅(qū)動(dòng)的 切入點(diǎn)。 它使用元數(shù)據(jù)屬性的值:典型地,使用源代碼級(jí)元數(shù)據(jù)。
5.2.3.2.?動(dòng)態(tài)切入點(diǎn)
動(dòng)態(tài)切入點(diǎn)的演算代價(jià)比靜態(tài)切入點(diǎn)高的多。它們不僅考慮靜態(tài)信息,還要考慮方法的?參數(shù)。這意味著它們必須在每次方法調(diào)用的時(shí)候都被計(jì)算;并且不能緩存結(jié)果 ,因?yàn)閰?shù)是變化的。
這個(gè)主要的例子就是控制流切入點(diǎn)。
5.2.3.2.1.?控制流切入點(diǎn)
Spring的控制流切入點(diǎn)概念上和AspectJ的cflow?切入點(diǎn)一致,雖然沒(méi)有其那么強(qiáng)大(當(dāng)前沒(méi)有辦法指定一個(gè)切入點(diǎn)在另一個(gè)切入點(diǎn)后執(zhí)行)。 一個(gè)控制流切入點(diǎn)匹配當(dāng)前的調(diào)用棧。例如,連接點(diǎn)被?com.mycompany.web包或者?SomeCaller類(lèi)中一個(gè)方法調(diào)用的時(shí)候,觸發(fā)該切入點(diǎn)。控制流切入點(diǎn)的實(shí)現(xiàn)類(lèi)是?org.springframework.aop.support.ControlFlowPointcut。
| 注意 | |
| 控制流切入點(diǎn)是動(dòng)態(tài)切入點(diǎn)中計(jì)算代價(jià)最高的。Java 1.4中, 它的運(yùn)行開(kāi)銷(xiāo)是其他動(dòng)態(tài)切入點(diǎn)的5倍。在Java 1.3中則超過(guò)10倍。 | |
5.2.4.?切入點(diǎn)超類(lèi)
Spring提供非常實(shí)用的切入點(diǎn)的超類(lèi)幫助你實(shí)現(xiàn)你自己的切入點(diǎn)。
因?yàn)殪o態(tài)切入點(diǎn)非常實(shí)用,你很可能子類(lèi)化StaticMethodMatcherPointcut,如下所示。 這只需要實(shí)現(xiàn)一個(gè)抽象方法(雖然可以改寫(xiě)其它的方法來(lái)自定義行為)。
class TestStaticPointcut extends StaticMethodMatcherPointcut {public boolean matches(Method m, Class targetClass) {// return true if custom criteria match} }當(dāng)然也有動(dòng)態(tài)切入點(diǎn)的超類(lèi)。
Spring 1.0 RC2或以上版本,自定義切入點(diǎn)可以用于任何類(lèi)型的通知。
5.2.5.?自定義切入點(diǎn)
因?yàn)镾pring中的切入點(diǎn)是Java類(lèi),而不是語(yǔ)言特性(如AspectJ),因此可以定義自定義切入點(diǎn), 無(wú)論靜態(tài)還是動(dòng)態(tài)。但是,沒(méi)有直接支持用AspectJ語(yǔ)法書(shū)寫(xiě)的復(fù)雜的切入點(diǎn)表達(dá)式。不過(guò), Spring的自定義切入點(diǎn)也可以任意的復(fù)雜。
后續(xù)版本的Spring可能象JA一樣C提供”語(yǔ)義切入點(diǎn)“的支持:例如,“所有更改目標(biāo)對(duì)象 實(shí)例變量的方法”。5.3.?Spring的通知類(lèi)型
現(xiàn)在讓我們看看Spring AOP是如何處理通知的。
5.3.1.?通知的生命周期
Spring的通知可以跨越多個(gè)被通知對(duì)象共享,或者每個(gè)被通知對(duì)象有自己的通知。這分別對(duì)應(yīng)?per-class或per-instance?通知。
Per-class通知使用最為廣泛。它適合于通用的通知,如事務(wù)adisor。它們不依賴(lài)被代理 的對(duì)象的狀態(tài),也不添加新的狀態(tài)。它們僅僅作用于方法和方法的參數(shù)。
Per-instance通知適合于導(dǎo)入,來(lái)支持混入(mixin)。在這種情況下,通知添加狀態(tài)到 被代理的對(duì)象。
可以在同一個(gè)AOP代理中混合使用共享和per-instance通知。
5.3.2.?Spring中通知類(lèi)型
Spring提供幾種現(xiàn)成的通知類(lèi)型并可擴(kuò)展提供任意的通知類(lèi)型。讓我們看看基本概念和 標(biāo)準(zhǔn)的通知類(lèi)型。
5.3.2.1.?Interception around advice
Spring中最基本的通知類(lèi)型是interception around advice?.
Spring使用方法攔截器的around通知是和AOP聯(lián)盟接口兼容的。實(shí)現(xiàn)around通知的 類(lèi)需要實(shí)現(xiàn)接口MethodInterceptor:
public interface MethodInterceptor extends Interceptor {Object invoke(MethodInvocation invocation) throws Throwable; }invoke()方法的MethodInvocation?參數(shù)暴露將被調(diào)用的方法、目標(biāo)連接點(diǎn)、AOP代理和傳遞給被調(diào)用方法的參數(shù)。?invoke()方法應(yīng)該返回調(diào)用的結(jié)果:連接點(diǎn)的返回值。
一個(gè)簡(jiǎn)單的MethodInterceptor實(shí)現(xiàn)看起來(lái)如下:
public class DebugInterceptor implements MethodInterceptor {public Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("Before: invocation=[" + invocation + "]");Object rval = invocation.proceed();System.out.println("Invocation returned");return rval;} }注意MethodInvocation的proceed()方法的調(diào)用。 這個(gè)調(diào)用會(huì)應(yīng)用到目標(biāo)連接點(diǎn)的攔截器鏈中的每一個(gè)攔截器。大部分?jǐn)r截器會(huì)調(diào)用這個(gè)方法,并返回它的返回值。但是, 一個(gè)MethodInterceptor,和任何around通知一樣,可以返回不同的值或者拋出一個(gè)異常,而 不調(diào)用proceed方法。但是,沒(méi)有好的原因你要這么做。
MethodInterceptor提供了和其他AOP聯(lián)盟的兼容實(shí)現(xiàn)的交互能力。這一節(jié)下面 要討論的其他的通知類(lèi)型實(shí)現(xiàn)了AOP公共的概念,但是以Spring特定的方式。雖然使用特定 通知類(lèi)型有很多優(yōu)點(diǎn),但如果你可能需要在其他的AOP框架中使用,請(qǐng)堅(jiān)持使用MethodInterceptor around通知類(lèi)型。注意目前切入點(diǎn)不能和其它框架交互操作,并且AOP聯(lián)盟目前也沒(méi)有定義切入 點(diǎn)接口。5.3.2.2.?Before通知
Before通知是一種簡(jiǎn)單的通知類(lèi)型。 這個(gè)通知不需要一個(gè)MethodInvocation對(duì)象,因?yàn)樗辉谶M(jìn)入一個(gè)方法 前被調(diào)用。
Before通知的主要優(yōu)點(diǎn)是它不需要調(diào)用proceed()?方法, 因此沒(méi)有無(wú)意中忘掉繼續(xù)執(zhí)行攔截器鏈的可能性。
MethodBeforeAdvice接口如下所示。 (Spring的API設(shè)計(jì)允許成員變量的before通知,雖然一般的對(duì)象都可以應(yīng)用成員變量攔截,但Spring 有可能永遠(yuǎn)不會(huì)實(shí)現(xiàn)它)。
public interface MethodBeforeAdvice extends BeforeAdvice {void before(Method m, Object[] args, Object target) throws Throwable; }注意返回類(lèi)型是void。 Before通知可以在連接點(diǎn)執(zhí)行之前 插入自定義的行為,但是不能改變返回值。如果一個(gè)before通知拋出一個(gè)異常,這將中斷攔截器 鏈的進(jìn)一步執(zhí)行。這個(gè)異常將沿著攔截器鏈后退著向上傳播。如果這個(gè)異常是unchecked的,或者 出現(xiàn)在被調(diào)用的方法的簽名中,它將會(huì)被直接傳遞給客戶(hù)代碼;否則,它將被AOP代理包裝到一個(gè)unchecked 的異常里。
下面是Spring中一個(gè)before通知的例子,這個(gè)例子計(jì)數(shù)所有正常返回的方法:
public class CountingBeforeAdvice implements MethodBeforeAdvice {private int count;public void before(Method m, Object[] args, Object target) throws Throwable {++count;}public int getCount() { return count; } } Before通知可以被用于任何類(lèi)型的切入點(diǎn)。5.3.2.3.?Throws通知
如果連接點(diǎn)拋出異常,Throws通知 在連接點(diǎn)返回后被調(diào)用。Spring提供強(qiáng)類(lèi)型的throws通知。注意這意味著?org.springframework.aop.ThrowsAdvice接口不包含任何方法: 它是一個(gè)標(biāo)記接口,標(biāo)識(shí)給定的對(duì)象實(shí)現(xiàn)了一個(gè)或多個(gè)強(qiáng)類(lèi)型的throws通知方法。這些方法形式 如下:
afterThrowing([Method], [args], [target], subclassOfThrowable)只有最后一個(gè)參數(shù)是必需的。 這樣從一個(gè)參數(shù)到四個(gè)參數(shù),依賴(lài)于通知是否對(duì)方法和方法 的參數(shù)感興趣。下面是throws通知的例子。
如果拋出RemoteException異常(包括子類(lèi)), 這個(gè)通知會(huì)被調(diào)用
public class RemoteThrowsAdvice implements ThrowsAdvice {public void afterThrowing(RemoteException ex) throws Throwable {// Do something with remote exception} }如果拋出ServletException異常, 下面的通知會(huì)被調(diào)用。和上面的通知不一樣,它聲明了四個(gè)參數(shù),所以它可以訪(fǎng)問(wèn)被調(diào)用的方法,方法的參數(shù) 和目標(biāo)對(duì)象:
public static class ServletThrowsAdviceWithArguments implements ThrowsAdvice {public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {// Do something will all arguments} }最后一個(gè)例子演示了如何在一個(gè)類(lèi)中使用兩個(gè)方法來(lái)同時(shí)處理?RemoteException和ServletException?異常。任意個(gè)數(shù)的throws方法可以被組合在一個(gè)類(lèi)中。
public static class CombinedThrowsAdvice implements ThrowsAdvice {public void afterThrowing(RemoteException ex) throws Throwable {// Do something with remote exception}public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {// Do something will all arguments} } Throws通知可被用于任何類(lèi)型的切入點(diǎn)。5.3.2.4.?After Returning通知
Spring中的after returning通知必須實(shí)現(xiàn)?org.springframework.aop.AfterReturningAdvice?接口,如下所示:
public interface AfterReturningAdvice extends Advice {void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable; }After returning通知可以訪(fǎng)問(wèn)返回值(不能改變)、被調(diào)用的方法、方法的參數(shù)和 目標(biāo)對(duì)象。
下面的after returning通知統(tǒng)計(jì)所有成功的沒(méi)有拋出異常的方法調(diào)用:
public class CountingAfterReturningAdvice implements AfterReturningAdvice {private int count;public void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable {++count;}public int getCount() {return count;} }這方法不改變執(zhí)行路徑。如果它拋出一個(gè)異常,這個(gè)異常而不是返回值將被沿著攔截器鏈 向上拋出。
After returning通知可被用于任何類(lèi)型的切入點(diǎn)。5.3.2.5.?Introduction通知
Spring將introduction通知看作一種特殊類(lèi)型的攔截通知。
Introduction需要實(shí)現(xiàn)IntroductionAdvisor, 和IntroductionInterceptor接口:
public interface IntroductionInterceptor extends MethodInterceptor {boolean implementsInterface(Class intf); }繼承自AOP聯(lián)盟MethodInterceptor接口的?invoke()方法必須實(shí)現(xiàn)導(dǎo)入:也就是說(shuō),如果被調(diào)用的方法是在 導(dǎo)入的接口中,導(dǎo)入攔截器負(fù)責(zé)處理這個(gè)方法調(diào)用,它不能調(diào)用proceed()?方法。
Introduction通知不能被用于任何切入點(diǎn),因?yàn)樗荒茏饔糜陬?lèi)層次上,而不是方法。 你可以只用InterceptionIntroductionAdvisor來(lái)實(shí)現(xiàn)導(dǎo)入通知,它有下面的方法:
public interface InterceptionIntroductionAdvisor extends InterceptionAdvisor {ClassFilter getClassFilter();IntroductionInterceptor getIntroductionInterceptor();Class[] getInterfaces(); }這里沒(méi)有MethodMatcher,因此也沒(méi)有和導(dǎo)入通知關(guān)聯(lián)的?切入點(diǎn)。只有類(lèi)過(guò)濾是合乎邏輯的。
getInterfaces()方法返回advisor導(dǎo)入的接口。
讓我們看看一個(gè)來(lái)自Spring測(cè)試套件中的簡(jiǎn)單例子。我們假設(shè)想要導(dǎo)入下面的接口到一個(gè) 或者多個(gè)對(duì)象中:
public interface Lockable {void lock();void unlock();boolean locked(); }這個(gè)例子演示了一個(gè)mixin。我們想要能夠 將被通知對(duì)象類(lèi)型轉(zhuǎn)換為L(zhǎng)ockable,不管它們的類(lèi)型,并且調(diào)用lock和unlock方法。如果我們調(diào)用 lock()方法,我們希望所有setter方法拋出LockedException異常。 這樣我們能添加一個(gè)方面使的對(duì)象不可變,而它們不需要知道這一點(diǎn):這是一個(gè)很好的AOP例 子。
首先,我們需要一個(gè)做大量轉(zhuǎn)化的IntroductionInterceptor。 在這里,我們繼承?org.springframework.aop.support.DelegatingIntroductionInterceptor?實(shí)用類(lèi)。我們可以直接實(shí)現(xiàn)IntroductionInterceptor接口,但是大多數(shù)情況下?DelegatingIntroductionInterceptor是最合適的。
DelegatingIntroductionInterceptor的設(shè)計(jì)是將導(dǎo)入 委托到真正實(shí)現(xiàn)導(dǎo)入接口的接口,隱藏完成這些工作的攔截器。委托可以使用構(gòu)造方法參數(shù) 設(shè)置到任何對(duì)象中;默認(rèn)的委托就是自己(當(dāng)無(wú)參數(shù)的構(gòu)造方法被使用時(shí))。這樣在下面的 例子里,委托是DelegatingIntroductionInterceptor的子類(lèi)?LockMixin。給定一個(gè)委托(默認(rèn)是自身)的?DelegatingIntroductionInterceptor實(shí)例尋找被這個(gè)委托(而不 是IntroductionInterceptor)實(shí)現(xiàn)的所有接口,并支持它們中任何一個(gè)導(dǎo)入。子類(lèi)如?LockMixin也可能調(diào)用suppressInterflace(Class intf)?方法隱藏不應(yīng)暴露的接口。然而,不管IntroductionInterceptor?準(zhǔn)備支持多少接口,IntroductionAdvisor將控制哪個(gè)接口將被實(shí)際 暴露。一個(gè)導(dǎo)入的接口將隱藏目標(biāo)的同一個(gè)接口的所有實(shí)現(xiàn)。
這樣,LockMixin繼承DelegatingIntroductionInterceptor?并自己實(shí)現(xiàn)Lockable。父類(lèi)自動(dòng)選擇支持導(dǎo)入的Lockable,所以我們不需要指定它。 用這種方法我們可以導(dǎo)入任意數(shù)量的接口。
注意locked實(shí)例變量的使用。這有效地添加額外的狀態(tài)到目標(biāo) 對(duì)象。
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {private boolean locked;public void lock() {this.locked = true;}public void unlock() {this.locked = false;}public boolean locked() {return this.locked;}public Object invoke(MethodInvocation invocation) throws Throwable {if (locked() && invocation.getMethod().getName().indexOf("set") == 0)throw new LockedException();return super.invoke(invocation);}}通常不要需要改寫(xiě)invoke()方法:實(shí)現(xiàn)?DelegatingIntroductionInterceptor就足夠了,如果是導(dǎo)入的方法,?DelegatingIntroductionInterceptor實(shí)現(xiàn)會(huì)調(diào)用委托方法, 否則繼續(xù)沿著連接點(diǎn)處理。在現(xiàn)在的情況下,我們需要添加一個(gè)檢查:在上鎖 狀態(tài)下不能調(diào)用setter方法。
所需的導(dǎo)入advisor是很簡(jiǎn)單的。只有保存一個(gè)獨(dú)立的?LockMixin實(shí)例,并指定導(dǎo)入的接口,在這里就是?Lockable。一個(gè)稍微復(fù)雜一點(diǎn)例子可能需要一個(gè)導(dǎo)入攔截器(可以 定義成prototype)的引用:在這種情況下,LockMixin沒(méi)有相關(guān)配置,所以我們簡(jiǎn)單地 使用new來(lái)創(chuàng)建它。
public class LockMixinAdvisor extends DefaultIntroductionAdvisor {public LockMixinAdvisor() {super(new LockMixin(), Lockable.class);} }我們可以非常簡(jiǎn)單地使用這個(gè)advisor:它不需要任何配置。(但是,有一點(diǎn)?是必要的:就是不可能在沒(méi)有IntroductionAdvisor?的情況下使用IntroductionInterceptor。) 和導(dǎo)入一樣,通常 advisor必須是針對(duì)每個(gè)實(shí)例的,并且是有狀態(tài)的。我們會(huì)有不同的的LockMixinAdvisor?每個(gè)被通知對(duì)象,會(huì)有不同的LockMixin。 advisor組成了被通知對(duì)象的狀態(tài)的一部分。
和其他advisor一樣,我們可以使用?Advised.addAdvisor()?方法以編程地方式使用這種advisor,或者在XML中配置(推薦這種方式)。 下面將討論所有代理創(chuàng)建,包括“自動(dòng)代理創(chuàng)建者”,選擇代理創(chuàng)建以正確地處理導(dǎo)入和有狀態(tài)的混入。
5.4.?Spring中的advisor
在Spring中,一個(gè)advisor就是一個(gè)aspect的完整的模塊化表示。 一般地,一個(gè)advisor包括通知和切入點(diǎn)。
撇開(kāi)導(dǎo)入這種特殊情況,任何advisor可被用于任何通知。?org.springframework.aop.support.DefaultPointcutAdvisor?是最通用的advisor類(lèi)。例如,它可以和MethodInterceptor、?BeforeAdvice或者ThrowsAdvice一起使 用。
Spring中可以將advisor和通知混合在一個(gè)AOP代理中。例如,你可以在一個(gè)代理配置中 使用一個(gè)對(duì)around通知、throws通知和before通知的攔截:Spring將自動(dòng)創(chuàng)建必要的攔截器鏈。
5.5.?用ProxyFactoryBean創(chuàng)建AOP代理
如果你在為你的業(yè)務(wù)對(duì)象使用Spring的IoC容器(例如ApplicationContext或者BeanFactory), 你應(yīng)該會(huì)或者你愿意會(huì)使用Spring的aop FactoryBean(記住,factory bean引入了一個(gè)間接層, 它能創(chuàng)建不同類(lèi)型的對(duì)象).
在spring中創(chuàng)建AOP proxy的基本途徑是使用org.springframework.aop.framework.ProxyFactoryBean. 這樣可以對(duì)pointcut和advice作精確控制。但是如果你不需要這種控制,那些簡(jiǎn)單的選擇可能更適合你。
5.5.1.?基本概要
ProxyFactoryBean,和其他Spring的?FactoryBean實(shí)現(xiàn)一樣,引入一個(gè)間接的層次。如果你 定義一個(gè)名字為foo的ProxyFactoryBean, 引用foo的對(duì)象所看到的不是ProxyFactoryBean?實(shí)例本身,而是由實(shí)現(xiàn)ProxyFactoryBean的類(lèi)的?getObject()方法所創(chuàng)建的對(duì)象。這個(gè)方法將創(chuàng)建一個(gè)包裝了目標(biāo)對(duì)象 的AOP代理。
使用ProxyFactoryBean或者其他IoC可知的類(lèi)來(lái)創(chuàng)建AOP代理 的最重要的優(yōu)點(diǎn)之一是IoC可以管理通知和切入點(diǎn)。這是一個(gè)非常的強(qiáng)大的功能,能夠?qū)?現(xiàn)其他AOP框架很難實(shí)現(xiàn)的特定的方法。例如,一個(gè)通知本身可以引用應(yīng)用對(duì)象(除了目標(biāo)對(duì)象, 它在任何AOP框架中都可以引用應(yīng)用對(duì)象),這完全得益于依賴(lài)注入所提供的可插入性。
5.5.2.?JavaBean的屬性
類(lèi)似于Spring提供的絕大部分FactoryBean實(shí)現(xiàn)一樣,?ProxyFactoryBean也是一個(gè)javabean,我們可以利用它的屬性來(lái):
-
指定你將要代理的目標(biāo)
-
指定是否使用CGLIB
一些關(guān)鍵屬性來(lái)自org.springframework.aop.framework.ProxyConfig?:它是所有AOP代理工廠(chǎng)的父類(lèi)。這些關(guān)鍵屬性包括:
-
proxyTargetClass: 如果我們應(yīng)該代理目標(biāo)類(lèi), 而不是接口,這個(gè)屬性的值為true。如果這是true,我們需要使用CGLIB。
-
optimize: 是否使用強(qiáng)優(yōu)化來(lái)創(chuàng)建代理。不要使用 這個(gè)設(shè)置,除非你了解相關(guān)的AOP代理是如何處理優(yōu)化的。目前這只對(duì)CGLIB代理有效;對(duì)JDK 動(dòng)態(tài)代理無(wú)效(默認(rèn))。
-
frozen: 是否禁止通知的改變,一旦代理工廠(chǎng)已經(jīng)配置。 默認(rèn)是false。
-
exposeProxy: 當(dāng)前代理是否要暴露在ThreadLocal中, 以便它可以被目標(biāo)對(duì)象訪(fǎng)問(wèn)。(它可以通過(guò)MethodInvocation得到,不需要ThreadLocal)。 如果一個(gè)目標(biāo)需要獲得它的代理并且exposeProxy的值是ture,可以使用?AopContext.currentProxy()方法。
-
aopProxyFactory: 所使用的AopProxyFactory具體實(shí)現(xiàn)。 這個(gè)參數(shù)提供了一條途徑來(lái)定義是否使用動(dòng)態(tài)代理、CGLIB還是其他代理策略。默認(rèn)實(shí)現(xiàn)將適當(dāng)?shù)剡x擇動(dòng)態(tài) 代理或CGLIB。一般不需要使用這個(gè)屬性;它的意圖是允許Spring 1.1使用另外新的代理類(lèi)型。
其他ProxyFactoryBean特定的屬性包括:
-
proxyInterfaces: 接口名稱(chēng)的字符串?dāng)?shù)組。如果這個(gè) 沒(méi)有提供,CGLIB代理將被用于目標(biāo)類(lèi)。
-
interceptorNames: Advisor、interceptor或其他 被應(yīng)用的通知名稱(chēng)的字符串?dāng)?shù)組。順序是很重要的。這里的名稱(chēng)是當(dāng)前工廠(chǎng)中bean的名稱(chēng),包 括來(lái)自祖先工廠(chǎng)的bean的名稱(chēng)。
-
singleton: 工廠(chǎng)是否返回一個(gè)單獨(dú)的對(duì)象,無(wú)論?getObject()被調(diào)用多少次。許多FactoryBean?的實(shí)現(xiàn)提供這個(gè)方法。默認(rèn)值是true。如果你想要使用有狀態(tài)的通知--例如,用于有狀態(tài)的 mixin--將這個(gè)值設(shè)為false,使用prototype通知。
5.5.3.?代理接口
讓我們來(lái)看一個(gè)簡(jiǎn)單的ProxyFactoryBean的實(shí)際例子。這個(gè)例子涉及到 :
-
一個(gè)將被代理的目標(biāo)bean,在這個(gè)例子里,這個(gè)bean的被定義為"personTarget".
-
一個(gè)advisor和一個(gè)interceptor來(lái)提供advice.
-
一個(gè)AOP代理bean定義,該bean指定目標(biāo)對(duì)象(這里是personTarget bean), 代理接口,和使用的advice.
請(qǐng)注意:person bean的interceptorNames屬性提供一個(gè)String列表, 列出的是該P(yáng)roxyFactoryBean使用的,在當(dāng)前bean工廠(chǎng)定義的interceptor或者advisor的 名字(advisor,interceptor,before,after returning,和throws advice 對(duì)象皆可)。 Advisor在該列表中的次序很重要。
你也許會(huì)對(duì)該列表為什么不采用bean的引用存有疑問(wèn)。 原因就在于如果ProxyFactoryBean的singleton屬性被設(shè)置為false, 那么bean工廠(chǎng)必須能返回多個(gè)獨(dú)立的代理實(shí)例。 如果有任何一個(gè)advisor本身是prototype的,那么它就需要返回獨(dú)立的實(shí)例, 也就是有必要從bean工廠(chǎng)獲取advisor的不同實(shí)例,bean的引用在這里顯然是不夠的。上面定義的“person”bean定義可以作為Person接口的實(shí)現(xiàn)來(lái)使用,如下所示:
Person person = (Person) factory.getBean("person");在同一個(gè)IoC的上下文中,其他的bean可以依賴(lài)于Person接口,就象依賴(lài)于一個(gè)普通的java對(duì)象一樣。
<bean id="personUser" class="com.mycompany.PersonUser"><property name="person"><ref local="person" /></property> </bean>在這個(gè)例子里,PersonUser類(lèi)暴露了一個(gè)類(lèi)型為Person的屬性。 只要是在用到該屬性的地方,AOP代理都能透明的替代一個(gè)真實(shí)的Person實(shí)現(xiàn)。 但是,這個(gè)類(lèi)可能是一個(gè)動(dòng)態(tài)代理類(lèi)。也就是有可能把它類(lèi)型轉(zhuǎn)換為一個(gè)Advised接口 (該接口在下面的章節(jié)中論述) 。
5.5.4.?代理類(lèi)
如果你需要代理的是類(lèi),而不是一個(gè)或多個(gè)接口,又該怎么辦呢?
想象一下我們上面的例子,如果沒(méi)有Person接口, 我們需要通知一個(gè)叫Person的類(lèi), 而且該類(lèi)沒(méi)有實(shí)現(xiàn)任何業(yè)務(wù)接口。在這種情況下,你可以配置Spring使用CGLIB代理, 而不是動(dòng)態(tài)代理。你只要在上面的ProxyFactoryBean定義中把 它的proxyTargetClass屬性改成true就行了。
只要你愿意,即使在有接口的情況下,你也可以強(qiáng)迫Spring使用CGLIB代理。
CGLIB代理是通過(guò)在運(yùn)行期產(chǎn)生目標(biāo)類(lèi)的子類(lèi)來(lái)進(jìn)行工作的。 Spring可以配置這個(gè)生成的子類(lèi),來(lái)代理原始目標(biāo)類(lèi)的方法調(diào)用。這個(gè)子類(lèi)是用?Decorator設(shè)計(jì)模式置入到advice中的。
CGLIB代理對(duì)于用戶(hù)來(lái)說(shuō)應(yīng)該是透明的。然而,還有以下一些因素需要考慮:
-
Final方法不能被通知,因?yàn)椴荒鼙恢貙?xiě)。
-
你需要在你的classpath中包括CGLIB的二進(jìn)制代碼,而動(dòng)態(tài)代理對(duì)任何JDK都是可用的.
CGLIB和動(dòng)態(tài)代理在性能上有微小的區(qū)別,對(duì)Spring 1.0來(lái)說(shuō),后者稍快。 另外,以后可能會(huì)有變化。在這種情況下性能不是決定性因素
5.6.?便利的代理創(chuàng)建方式
通常,我們不需要ProxyFactoryBean的全部功能,因?yàn)槲覀兂3V粚?duì)一個(gè)方面感興趣: 例如,事務(wù)管理。
當(dāng)我們僅僅對(duì)一個(gè)特定的方面干興趣時(shí),我們可以使用許多便利的工廠(chǎng)來(lái)創(chuàng)建AOP代理。這些在其他 章節(jié)討論,所以這里我們快速瀏覽一下它們。
5.6.1.?TransactionProxyFactoryBean
用Spring提供的jPetStore的示例應(yīng)用 演示了TransactionProxyFactoryBean的使用方式。
TransactionProxyFactoryBean是ProxyConfig的子類(lèi), 因此基本配置信息是和ProxyFactoryBean共享的。 (見(jiàn)上面ProxyConfig的屬性列表。)
下面的代碼來(lái)自于JPetStore application,演示了ProxyFactoryBean是如何工作的。 跟ProxyFactoryBean一樣,存在一個(gè)目標(biāo)bean的定義。 類(lèi)的依賴(lài)關(guān)系定義在代理工廠(chǎng)bean定義中(petStore),而不是普通Java對(duì)象(petStoreTarget)。
TransactionProxyFactoryBean需要設(shè)置一個(gè)target屬性, 還需要設(shè)置“transactionAttributes”, “transactionAttributes”用來(lái)指定需要事務(wù)化 處理的方法,還有要求的傳播方式和其他設(shè)置:
<bean id="petStoreTarget" class="org.springframework.samples.jpetstore.domain.logic.PetStoreImpl"><property name="accountDao"><ref bean="accountDao"/></property><!-- Other dependencies omitted --> </bean><bean id="petStore" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><property name="transactionManager"><ref bean="transactionManager"/></property><property name="target"><ref local="petStoreTarget"/></property><property name="transactionAttributes"><props><prop key="insert*">PROPAGATION_REQUIRED</prop><prop key="update*">PROPAGATION_REQUIRED</prop><prop key="*">PROPAGATION_REQUIRED,readOnly</prop></props></property> </bean>TransactionProxyFactoryBean自動(dòng)創(chuàng)建一個(gè)事務(wù)advisor, 該advisor包括一個(gè)基于事務(wù)屬性的切入點(diǎn)。因此只有事務(wù)性的方法被通知。
TransactionProxyFactoryBean使用preInterceptors和 postInterceptors屬性指定“pre”和“post”通知。它們將攔截器,通知和Advisor數(shù)組放置 在事務(wù)攔截器前后的攔截器鏈中。使用XML格式的bean定義中的<list>元素定義,就 象下面一樣:
<property name="preInterceptors"><list><ref local="authorizationInterceptor"/><ref local="notificationBeforeAdvice"/></list> </property> <property name="postInterceptors"><list><ref local="myAdvisor"/></list> </property>這些屬性可以加到上面的“petStore”的bean定義里。一個(gè)通用用法是將事務(wù)和聲明式 安全組合在一起使用:一個(gè)和EJB提供的類(lèi)似的方法。
因?yàn)槭褂们皵r截器和后攔截器時(shí),用的是真正的實(shí)例引用,而不象在?ProxyFactoryBean中用的bean的名字,因此它們只能用于共享實(shí)例的通知。 因此它們不能用在有狀態(tài)的通知中:例如,在mixin中。這和TransactionProxyFactoryBean的要求是 一致的。如果你需要更復(fù)雜的,可以定制的AOP,你可以考慮使用普通的ProxyFactoryBean, 或者是自動(dòng)代理生成器(參考下面)。
尤其是如果我們將Spring的AOP在許多情況下看成是EJB的替代品,我們會(huì)發(fā)現(xiàn)大多數(shù)通知是很普通的, 可以使用共享實(shí)例。聲明式的事務(wù)管理和安全檢查是一個(gè)典型的例子。TransactionProxyFactoryBean依賴(lài)于由它的transactionManager?屬性指定的TransactionManager。 這種事務(wù)管理方式是可插拔的,基于JTA,JDBC或者其他事務(wù)管理策略皆可。 這與Spring的事務(wù)抽象層有關(guān),而不在于AOP本身。我們將在下一章中討論事務(wù)機(jī)制。
如果你只對(duì)聲明性事務(wù)管理感興趣,TransactionProxyFactoryBean是一個(gè)不錯(cuò)的解決辦法, 并且比直接使用ProxyFactoryBean來(lái)得簡(jiǎn)單.5.6.2.?EJB 代理
其它有一些專(zhuān)門(mén)的代理用于創(chuàng)建EJB代理,使得EJB的“業(yè)務(wù)方法”的接口可以被調(diào)用代碼直接使用。 調(diào)用代碼并不需要進(jìn)行JNDI查找或使用EJB的創(chuàng)建方法:這是在可讀性和架構(gòu)靈活性方面的重大提高。
進(jìn)一步請(qǐng)參考本手冊(cè)內(nèi)的Spring的EJB業(yè)務(wù)。
5.7.?使用ProxyFactory以編程的方式創(chuàng)建AOP代理
使用Spring以編程的方式創(chuàng)建AOP代理也很簡(jiǎn)單。 這使得你不需要Spring的IoC就能夠使用Spring的AOP。
下面的代碼顯示的用攔截器和advisor為目標(biāo)對(duì)象創(chuàng)建代理。目標(biāo)對(duì)象實(shí)現(xiàn)的接口將自動(dòng)被代理:
ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl); factory.addInterceptor(myMethodInterceptor); factory.addAdvisor(myAdvisor); MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();第一步是創(chuàng)建類(lèi)型為org.springframework.aop.framework.ProxyFactory?的對(duì)象。你可以和上面的例子一樣用目標(biāo)對(duì)象創(chuàng)建,或者在另一個(gè)構(gòu)造函數(shù)中指定要被代理的接口。
你可以添加攔截器或advisor,在整個(gè)ProxyFactory的生命周期內(nèi)操作它們。如果你添加 IntroductionInterceptionAroundAdvisor,你可以使代理實(shí)現(xiàn)附加接口。
ProxyFactory(它是從AdvisedSupport繼承而來(lái))也提供了一些實(shí)用方法,使你可以添加 其它通知類(lèi)型,比如before通知和throws通知。AdvisedSupport是ProxyFactory和ProxyFactoryBean 的父類(lèi)。
將AOP代理的創(chuàng)建和IoC框架結(jié)合起來(lái)在大多數(shù)應(yīng)用中都是最好的實(shí)現(xiàn)方式。我們推薦你和一般情況一樣, 不要將AOP配置信息放在Java代碼里。5.8.?操作被通知對(duì)象
無(wú)論你怎么創(chuàng)建AOP代理,你都可以使用org.springframework.aop.framework.Advised?接口來(lái)操作它們。任何AOP代理無(wú)論實(shí)現(xiàn)其它什么接口,都可以類(lèi)型轉(zhuǎn)換為這個(gè)接口。這個(gè)接口包括下列方法:
void addInterceptor(Interceptor interceptor) throws AopConfigException;void addInterceptor(int pos, Interceptor interceptor) throws AopConfigException;void addAdvisor(Advisor advisor) throws AopConfigException;void addAdvisor(int pos, Advisor advisor) throws AopConfigException;int indexOf(Advisor advisor);boolean removeAdvisor(Advisor advisor) throws AopConfigException;void removeAdvisor(int index) throws AopConfigException;boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;boolean isFrozen();getAdvisors()方法為工廠(chǎng)中的每個(gè)advisor,攔截器或者其它通知類(lèi)型返回一個(gè)Advisor。 如果你添加一個(gè)Advisor,使用當(dāng)前索引返回的advisor就是你添加的對(duì)象。如果你添加攔截器或其它通知類(lèi)型, Spring將當(dāng)前對(duì)象和一個(gè)滿(mǎn)足要求的切入點(diǎn)封裝在一個(gè)advisor里。因此,如果你添加?MethodInterceptor, 使用當(dāng)前索引返回的advisor是一個(gè)DefaultPointcutAdvisor,這個(gè)advisor返回?MethodInterceptor和滿(mǎn)足所有類(lèi)和方法的切入點(diǎn)。
addAdvisor()被用來(lái)添加Advisor。通常會(huì)是一個(gè)普通的?DefaultPointcutAdvisor,它可以和任何通知或切入點(diǎn)(除了引用)一起使用。
缺省情況下,在每次代理被創(chuàng)建的時(shí)候添加或刪除advisor或攔截器。唯一的限制是不能添加或刪除 引入advisor,因?yàn)楣S(chǎng)提供的已存在的代理不反映接口的變化。(你可以從工廠(chǎng)得到一個(gè)新的代理來(lái)避免這個(gè)問(wèn)題)
是否建議在產(chǎn)品中修改業(yè)務(wù)對(duì)象的通知還值得懷疑,雖然毫無(wú)疑問(wèn)存在合理的使用情況。但是, 在開(kāi)發(fā)中這是非常有用的:例如,在測(cè)試中。我有時(shí)候發(fā)現(xiàn)以攔截器或其它通知的形式來(lái)添加測(cè)試代碼非常有用, 這樣就可以進(jìn)入我想要測(cè)試的方法調(diào)用。(例如,通知可以進(jìn)入為這個(gè)方法創(chuàng)建的事務(wù)中: 在為回滾事務(wù)作標(biāo)記前,運(yùn)行SQL檢查數(shù)據(jù)庫(kù)是否被正確更新。)根據(jù)你創(chuàng)建代理的方式,你通常可以設(shè)置frozen標(biāo)記,這樣Advised?的isFrozen()就返回true,任何添加或刪除通知都將導(dǎo)致?AopConfigException。這種凍結(jié)被通知對(duì)象狀態(tài)的方法在一些情況下是非常有用的: 例如,為了阻止調(diào)用代碼刪除一個(gè)安全攔截器。如果已知運(yùn)行時(shí)修改通知不被允許,這還可以被Spring 1.1用來(lái) 作優(yōu)化。
5.9.?使用“autoproxy”功能
目前為止,我們已經(jīng)討論了使用ProxyFactoryBean或類(lèi)似的工廠(chǎng)bean來(lái)顯式創(chuàng)建AOP代理。
Spring也允許我們使用“autoproxy”的bean定義,它可以自動(dòng)代理所選擇的bean定義。這是建立在Spring的 “bean后處理器”機(jī)制上的,它能夠在容器載入bean定義的時(shí)候修改任何bean定義。
在這個(gè)模型中,你可以在你的XML bean定義文件中建立特殊的bean定義,來(lái)配置自動(dòng)代理機(jī)制。這允許你聲明目標(biāo)對(duì)象以使用自動(dòng)代理功能: 你就可以不需要使用ProxyFactoryBean。
有兩種方法來(lái)實(shí)現(xiàn)自動(dòng)代理:
-
使用一個(gè)自動(dòng)代理生成器,它引用當(dāng)前上下文中的那些特殊bean
-
有一個(gè)特殊的自動(dòng)代理創(chuàng)建的情況值得單獨(dú)考慮:由源代碼級(jí)元數(shù)據(jù)驅(qū)動(dòng)的自動(dòng)代理創(chuàng)建
5.9.1.?自動(dòng)代理的bean定義
org.springframework.aop.framework.autoproxy包提供了下列標(biāo)準(zhǔn)自動(dòng)代理生成器。
5.9.1.1.?BeanNameAutoProxyCreator
BeanNameAutoProxyCreator為名字符合某個(gè)值或統(tǒng)配符的bean自動(dòng)創(chuàng)建AOP代理。
<bean id="jdkBeanNameProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"><property name="beanNames"><value>jdk*,onlyJdk</value></property><property name="interceptorNames"><list><value>myInterceptor</value></list></property> </bean>就和ProxyFactoryBean一樣,有一個(gè)interceptorNames?屬性,而不是一個(gè)攔截器列表,這個(gè)屬性允許為prototype的advisor提供正確的行為。雖然名字叫 “攔截器”,但是也可以是advisor或任何通知類(lèi)型。
就象一般的自動(dòng)代理創(chuàng)建一樣,使用BeanNameAutoProxyCreator的主要目的 是對(duì)多個(gè)對(duì)象使用相同的配置信息,并且減少配置的工作量。這在為多個(gè)對(duì)象使用聲明式事務(wù)時(shí)是一個(gè)很流行的選擇。
在上面的例子中,名字匹配的bean定義,如“jdkMyBean”和“onlyJdk”,是包含目標(biāo)類(lèi)的普通bean定義。?BeanNameAutoProxyCreator將自動(dòng)創(chuàng)建AOP代理。相同的通知會(huì)被因用到所有匹配的bean。 注意,如果使用了advisor(而不是上面例子中的攔截器),切入點(diǎn)可能對(duì)不同的bean會(huì)不同。
5.9.1.2.?DefaultAdvisorAutoProxyCreator
DefaultAdvisorAutoProxyCreator是一個(gè)更通用,更強(qiáng)大的自動(dòng)代理生成器。它將 自動(dòng)應(yīng)用于當(dāng)前上下文的符合條件的advisor,而不需要在自動(dòng)代理advisor的bean定義中包含特定的bean名字。 它有助于配置的一致性,并避免象BeanNameAutoProxyCreator一樣重復(fù)配置。
使用這個(gè)機(jī)制包括:
-
指定一個(gè)DefaultAdvisorAutoProxyCreator的bean定義
-
在相同或相關(guān)上下文中指定任何數(shù)目的Advisor。注意這些必須是Advisor, 而不僅僅是攔截器或其它通知。這是很必要的,因?yàn)楸仨氂幸粋€(gè)切入點(diǎn)來(lái)檢查每個(gè)通知是否符合候選bean定義。
DefaultAdvisorAutoProxyCreator會(huì)自動(dòng)計(jì)算每個(gè)advisor包含的的切入點(diǎn),看看 是否有什么通知應(yīng)該被引用到每個(gè)業(yè)務(wù)對(duì)象(比如例子中的“businessObject1”和“businessObject2”)。
這意味著任何數(shù)目的advisor都可以自動(dòng)應(yīng)用到每個(gè)業(yè)務(wù)對(duì)象。如果advisor中沒(méi)有任何切入點(diǎn)符合業(yè)務(wù)對(duì)象的 方法,這個(gè)對(duì)象就不會(huì)被代理。因?yàn)闀?huì)為新的業(yè)務(wù)對(duì)象添加bean定義,如果必要,它們會(huì)自動(dòng)被代理。
一般來(lái)說(shuō),自動(dòng)代理可以保證調(diào)用者或依賴(lài)無(wú)法接觸未被通知的對(duì)象。在這個(gè)ApplicationContext上 調(diào)用getBean("businessObject1")返回一個(gè)AOP代理,而不是目標(biāo)業(yè)務(wù)對(duì)象。
<bean id="autoProxyCreator"class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"> </bean><bean id="txAdvisor"autowire="constructor"class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor"><property name="order"><value>1</value></property> </bean><bean id="customAdvisor"class="com.mycompany.MyAdvisor"> </bean><bean id="businessObject1"class="com.mycompany.BusinessObject1"><!-- Properties omitted --> </bean><bean id="businessObject2"class="com.mycompany.BusinessObject2"> </bean>如果你想在幾個(gè)業(yè)務(wù)對(duì)象上應(yīng)用相同的通知,DefaultAdvisorAutoProxyCreator?就非常有用。一旦定義恰當(dāng),你可以簡(jiǎn)單地添加業(yè)務(wù)對(duì)象而不需要包括特定的代理配置。你也可以非常容易地 刪除所附加的方面--例如,跟蹤或性能監(jiān)控的方面--可以盡可能減少配置修改。
DefaultAdvisorAutoProxyCreator支持過(guò)濾(使用命名規(guī)則以便只計(jì)算某一些 advisor,允許在一個(gè)工廠(chǎng)中使用多個(gè),被不同配置的AdvisorAutoProxyCreator)和排序。Advisor可以實(shí)現(xiàn)?org.springframework.core.Ordered接口以保證正確的排序,如果排序確實(shí)需要。在 上面的例子中,TransactionAttributeSourceAdvisor有一個(gè)可配置的順序值,缺損是不排序。
5.9.1.3.?AbstractAdvisorAutoProxyCreator
這是DefaultAdvisorAutoProxyCreator的父類(lèi)。你可以繼承它實(shí)現(xiàn)你自己的自動(dòng)代理生成器,這種情況不太常見(jiàn), 一般是advisor定義不能給DefaultAdvisorAutoProxyCreator框架的行為提供足夠的定制。
5.9.2.?使用元數(shù)據(jù)驅(qū)動(dòng)的自動(dòng)代理
一種特別重要的自動(dòng)代理類(lèi)型是由元數(shù)據(jù)驅(qū)動(dòng)的。這和.NET的ServicedComponents編程框架 非常類(lèi)似。它沒(méi)有象EJB那樣使用XML部署描述,事務(wù)管理和其它企業(yè)級(jí)業(yè)務(wù)的配置都是定義在源代碼級(jí)的屬性上。
在這種情況下,你可以使用DefaultAdvisorAutoProxyCreator,以及可以讀取元數(shù)據(jù)屬性的 Advisor。元數(shù)據(jù)細(xì)節(jié)定義在候選advisor的切入點(diǎn)部分,而不是自動(dòng)代理創(chuàng)建類(lèi)本身。
這是DefaultAdvisorAutoProxyCreator的一種特殊情況,但是它本身而言是值得考慮的。 (可以讀取元數(shù)據(jù)的代碼處于advisor的切入點(diǎn)中,而不是AOP框架本身。)
jPetStore示例應(yīng)用的/attributes目錄演示了屬性驅(qū)動(dòng)的自動(dòng)代理的使用。在這個(gè)例子中, 沒(méi)有必要使用TransactionProxyFactoryBean。僅僅在業(yè)務(wù)對(duì)象上定義業(yè)務(wù)屬性就足夠了,因?yàn)?使用了可知元數(shù)據(jù)的切入點(diǎn)。bean定義在/WEB-INF/declarativeServices.xml中,包括下 面的代碼。注意這是通用的,可以在jPetStore以外的地方使用:
<bean id="autoproxy" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"> </bean><bean id="transactionAttributeSource"class="org.springframework.transaction.interceptor.AttributesTransactionAttributeSource"autowire="constructor"> </bean><bean id="transactionInterceptor"class="org.springframework.transaction.interceptor.TransactionInterceptor"autowire="byType"> </bean><bean id="transactionAdvisor"class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor"autowire="constructor" > </bean><bean id="attributes"class="org.springframework.metadata.commons.CommonsAttributes" />DefaultAdvisorAutoProxyCreator?bean定義--在這種情況下稱(chēng)作“advisor”,但是名字 無(wú)關(guān)緊要--會(huì)在當(dāng)前的應(yīng)用上午中選擇所有符合的切入點(diǎn)。在這個(gè)例子中,類(lèi)型為?TransactionAttributeSourceAdvisor的“transactionAdvisor”bean定義將會(huì)應(yīng)用于包含事務(wù)屬性 的類(lèi)或方法。TransactionAttributeSourceAdvisor通過(guò)構(gòu)造函數(shù)依賴(lài)于TransactionInterceptor。這個(gè)例子通過(guò)自動(dòng) 裝配來(lái)解析它。AttributesTransactionAttributeSource依賴(lài)于?org.springframework.metadata.Attributes接口的一個(gè)實(shí)現(xiàn)。在這段代碼中,“attributes” bean使用Jakarta Commons Attributes API來(lái)獲取屬性信息。(應(yīng)用代碼必須使用Commons Attributes編譯任務(wù)編譯。)
這里定義的TransactionInterceptor依賴(lài)于一個(gè)?PlatformTransactionManager定義,它并沒(méi)有被包括在這個(gè)通用的文件中(雖然應(yīng)該是這樣), 這是因?yàn)樗呛蛻?yīng)用的事務(wù)需求相關(guān)的(一般地,是想這個(gè)例子中的JTA,或者Hibernate,JDO 或JDBC):
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/> 如果你只要求聲明式事務(wù)管理,使用這些通用的XML定義就可以使得Spring自動(dòng)代理含有事務(wù)屬性的所有類(lèi)和方法。 你不需要直接和AOP打交道,并且編程模型和.NET的ServicedComponents非常相似。這個(gè)機(jī)制具有可擴(kuò)展性。它可以基于定制的屬性來(lái)使用自動(dòng)代理。你需要:
-
定義你的定制屬性。
-
指定的Advisor包含必要的通知和由方法或類(lèi)的定制屬性所觸發(fā)的切入點(diǎn)。你可以使用已經(jīng)存在的通知,僅僅實(shí) 現(xiàn)用來(lái)選擇定制屬性的切入點(diǎn)。
這些advisor可能對(duì)每個(gè)被通知類(lèi)都是唯一的(例如,maxin)。它們僅僅需要被定義成 prototype bean,而不是singleton bean。例如,Spring的測(cè)試套件中的LockMixin?引入攔截器可以和一個(gè)屬性驅(qū)動(dòng)切入點(diǎn)一起來(lái)定位一個(gè)maxin,就象這里演示的。我們使用JavaBean配置的 普通的DefaultPointcutAdvisor:
<bean id="lockMixin"class="org.springframework.aop.LockMixin"singleton="false" /><bean id="lockableAdvisor"class="org.springframework.aop.support.DefaultPointcutAdvisor"singleton="false" ><property name="pointcut"><ref local="myAttributeAwarePointcut"/></property><property name="advice"><ref local="lockMixin"/></property> </bean><bean id="anyBean" class="anyclass" ...如果知道屬性的切入點(diǎn)符合anyBean或者其它bean定義中的任何方法,這個(gè)maxin 將被應(yīng)用。注意,lockMixin和lockableAdvisor定義都是 prototype的。myAttributeAwarePointcut切入點(diǎn)可以被定義成singleton,因?yàn)樗粸?不同的被通知對(duì)象保存狀態(tài)。
5.10.?使用TargetSources
Spring提供了TargetSource的概念,由?org.springframework.aop.TargetSource接口定義。這個(gè)接口負(fù)責(zé)返回實(shí)現(xiàn)切入點(diǎn)的 “目標(biāo)對(duì)象”。每次AOP代理處理方法調(diào)用時(shí),目標(biāo)實(shí)例都會(huì)用到TargetSource實(shí)現(xiàn)。
使用Spring AOP的開(kāi)發(fā)者一般不需要直接使用TargetSources,但是這提供了一種強(qiáng)大的方法來(lái)支持池,熱交換, 和其它復(fù)雜目標(biāo)。例如,一個(gè)支持池的TargetSource可以在每次調(diào)用時(shí)返回不同的目標(biāo)對(duì)象實(shí)例,使用池來(lái)管理實(shí)例。
如果你沒(méi)有指定TargetSource,就使用缺省的實(shí)現(xiàn),它封裝了一個(gè)本地對(duì)象。每次調(diào)用會(huì)返回相同的目標(biāo)對(duì)象 (和你期望的一樣)。
讓我們來(lái)看一下Spring提供的標(biāo)準(zhǔn)目標(biāo)源,以及如何使用它們。
當(dāng)使用定制目標(biāo)源時(shí),你的目標(biāo)通常需要定義為prototype bean,而不是singleton bean。這使得 Spring在需要的時(shí)候創(chuàng)建一個(gè)新的目標(biāo)實(shí)例。5.10.1.?可熱交換的目標(biāo)源
org.springframework.aop.target.HotSwappableTargetSource?允許切換一個(gè)AOP代理的目標(biāo),而調(diào)用者維持對(duì)它的引用。
修改目標(biāo)源的目標(biāo)會(huì)立即起作用。并且HotSwappableTargetSource是線(xiàn)程安全的。
你可以通過(guò)HotSwappableTargetSource的swap()方法 來(lái)改變目標(biāo),就象下面一樣:
HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper"); Object oldTarget = swapper.swap(newTarget);所需的XML定義如下:
<bean id="initialTarget" class="mycompany.OldTarget"> </bean><bean id="swapper" class="org.springframework.aop.target.HotSwappableTargetSource"><constructor-arg><ref local="initialTarget"/></constructor-arg> </bean><bean id="swappable" class="org.springframework.aop.framework.ProxyFactoryBean" ><property name="targetSource"><ref local="swapper"/></property> </bean>上面的swap()調(diào)用會(huì)修改swappable這個(gè)bean的目標(biāo)。持有對(duì)這個(gè)bean應(yīng)用的客戶(hù)端 將不會(huì)知道這個(gè)變化,但會(huì)立刻轉(zhuǎn)為使用新的目標(biāo)對(duì)象。
雖然這個(gè)例子沒(méi)有添加任何通知--使用TargetSource也沒(méi)必要添加通知-- 當(dāng)然任何TargetSource都可以和任何一種通知一起使用。
5.10.2.?支持池的目標(biāo)源
使用支持池的目標(biāo)源提供了一種和無(wú)狀態(tài)的session EJB類(lèi)似的編程模式,在無(wú)狀態(tài)的session EJB中,維護(hù)了 一個(gè)相同實(shí)例的池,提供從池中獲取可用對(duì)象的方法。
Spring的池和SLSB的池之間的重要區(qū)別在于Spring的池可以被應(yīng)用到任何普通Java對(duì)象。就象Spring的通用 的做法,這個(gè)業(yè)務(wù)也可以以非侵入的方式被應(yīng)用。
Spring直接支持Jakarta Commons Pool 1.1,它是一種非常高效的池實(shí)現(xiàn)。使用這個(gè)功能,你需要在你的應(yīng)用的 classpath中添加commons-pool的Jar文件。也可以直接繼承?org.springframework.aop.target.AbstractPoolingTargetSource來(lái)支持其它池API。
下面是一個(gè)配置的例子:
<bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject" singleton="false">... properties omitted </bean><bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPoolTargetSource"><property name="targetBeanName"><value>businessObject</value></property><property name="maxSize"><value>25</value></property> </bean><bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean" ><property name="targetSource"><ref local="poolTargetSource"/></property><property name="interceptorNames"><value>myInterceptor</value></property> </bean>注意例子中的目標(biāo)對(duì)象“businessObjectTarget”必須是prototype。這樣在?PoolingTargetSource的實(shí)現(xiàn)在擴(kuò)大池容量的時(shí)候可以創(chuàng)建目標(biāo)的新實(shí)例。關(guān)于這些屬性的 信息可以參考AbstractPoolingTargetSource和子類(lèi)的Javadoc。maxSize是最基本的屬性, 被保證總是存在。
在這種情況下,名字為“myInterceptor”的攔截器需要定義在同一個(gè)IoC上下文中。但是,并不一定需要 指定攔截器也用池。如果你僅需要池,并且沒(méi)有其它通知,可以根本不設(shè)置屬性interceptorNames。
也可以配置Spring以便可以將任何池化的對(duì)象轉(zhuǎn)換類(lèi)型為?org.springframework.aop.target.PoolingConfig接口。通過(guò)這個(gè)接口的一個(gè)引入,可以得到 配置信息和池的當(dāng)前大小。你需要這樣定義一個(gè)advisor:
<bean id="poolConfigAdvisor" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"><property name="target"><ref local="poolTargetSource" /></property><property name="targetMethod"><value>getPoolingConfigMixin</value></property> </bean>通過(guò)調(diào)用AbstractPoolingTargetSource類(lèi)上的方法,可以得到這個(gè)advisor, 因此使用MethodInvokingFactoryBean。這個(gè)advisor的名字(“poolConfigAdvisor”)必須在暴露池化對(duì)象的 This advisor is obtained by calling a convenience method on the ProxyFactoryBean中的攔截器名字列表中。
這個(gè)類(lèi)型轉(zhuǎn)換就象下面:
PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject"); System.out.println("Max pool size is " + conf.getMaxSize()); 池化無(wú)狀態(tài)業(yè)務(wù)對(duì)象并不是總是必要的。我們不認(rèn)為這是缺省選擇,因?yàn)榇蠖鄶?shù)無(wú)狀態(tài)對(duì)象自然就是線(xiàn)程 安全的,如果資源被緩存,實(shí)例池化會(huì)有問(wèn)題。簡(jiǎn)單的池也可以使用自動(dòng)代理。任何自動(dòng)代理生成器都可以設(shè)置TargetSources。
5.10.3.?Prototype目標(biāo)源
設(shè)置“prototype”目標(biāo)源和支持池的目標(biāo)源類(lèi)似。在每次方法調(diào)用的時(shí)候都會(huì)創(chuàng)建一個(gè)新的目標(biāo)實(shí)例。 雖然在現(xiàn)代JVM中創(chuàng)建對(duì)象的代價(jià)不是很高,但是裝配新對(duì)象的代價(jià)可能更高(為了maz滿(mǎn)足它的IoC依賴(lài)關(guān)系)。 因此沒(méi)有好的理由不應(yīng)該使用這個(gè)方法。
為了這么做,你可以修改上面的的poolTargetSource定義,就向下面一樣。 (為了清晰起見(jiàn),我修改了名字。)
<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource"><property name="targetBeanName"><value>businessObject</value></property> </bean>只有一個(gè)屬性:目標(biāo)bean的名字。在TargetSource實(shí)現(xiàn)中使用繼承是為了保證命名的一致性。就象支持池的 目標(biāo)源一樣,目標(biāo)bean必須是一個(gè)prototype的bean定義。
5.11.?定義新的通知類(lèi)型
Spring AOP設(shè)計(jì)能夠很容易地?cái)U(kuò)展。雖然攔截實(shí)現(xiàn)的策略目前只在內(nèi)部使用,但還是有可能支持?jǐn)r截around通知, before通知,throws通知和after returning通知以外的任何通知類(lèi)型。
org.springframework.aop.framework.adapter?包是一個(gè)支持添加新的定制通知類(lèi)型而不修改核心框架的SPI(譯:可能是API)包。定制通知類(lèi)型的唯一限制是它必須實(shí)現(xiàn)?org.aopalliance.aop.Advice標(biāo)記接口。
更多信息請(qǐng)參考o(jì)rg.springframework.aop.framework.adapter包的Javadoc。
5.12.?進(jìn)一步的資料和資源
對(duì)于AOP的介紹,我推薦Ramnivas Laddad (Manning, 2003)寫(xiě)的AspectJ in Action。
進(jìn)一步的Spring AOP的例子請(qǐng)參考Spring的示例應(yīng)用:
-
JPetStore的缺省配置演示了使用TransactionProxyFactoryBean來(lái)定義聲明式事務(wù)管理。
-
JPetStore的/attributes目錄演示了屬性驅(qū)動(dòng)的聲明式事務(wù)管理。
如果你對(duì)Spring AOP更多高級(jí)功能感興趣,可以看一下測(cè)試套件。測(cè)試覆蓋率超過(guò)90%,并且演示了本文檔沒(méi)有提到 的許多高級(jí)功能。
5.13.?路標(biāo)
Spring AOP,就象Spring的其它部分,是開(kāi)發(fā)非常活躍的部分。核心API已經(jīng)穩(wěn)定了。象Spring的其它部分一樣, AOP框架是非常模塊化的,在保留基礎(chǔ)設(shè)計(jì)的同時(shí)提供擴(kuò)展。在Spring 1.1到1.2階段有很多地方可能會(huì)有所提高,但是這 些地方也保留了向后兼容性。它們是:
-
性能的提高:AOP代理的創(chuàng)建由工廠(chǎng)通過(guò)策略接口處理。因此我們能夠支持額外的AOP 代理類(lèi)型而不影響用戶(hù)代碼或核心實(shí)現(xiàn)。對(duì)于Spring 1.1,我們正在檢查AOP代理實(shí)現(xiàn)的所有字節(jié)碼,萬(wàn)一不需要 運(yùn)行時(shí)通知改變。這應(yīng)該大大減少AOP框架的額外操作。但是注意,AOP框架的額外操作不是在普通使用中需要考慮 的內(nèi)容。
-
更具表達(dá)力的切入點(diǎn):Spring目前提供了一個(gè)具有表達(dá)力的切入點(diǎn)接口,但是我們 添加更多的切入點(diǎn)實(shí)現(xiàn)。我們正在考慮提供一個(gè)簡(jiǎn)單但具有強(qiáng)大表達(dá)式語(yǔ)言的實(shí)現(xiàn)。如果你希望貢獻(xiàn)一個(gè)有用的 切入點(diǎn)實(shí)現(xiàn),我們將非常歡迎。
-
引入方面這個(gè)高層概念,它包含多個(gè)advisor。
總結(jié)
以上是生活随笔為你收集整理的Spring - Java/J2EE Application Framework 应用框架 第 5 章 Spring AOP: Spring之面向方面编程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Spring - Java/J2EE A
- 下一篇: Spring - Java/J2EE A