欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
使用Spring進(jìn)行面向切面編程

6.1. 簡(jiǎn)介

面向切面編程AOP)提供另外一種角度來(lái)思考程序結構,通過(guò)這種方式彌補了面向對象編程(OOP)的不足。 除了類(lèi)(classes)以外,AOP提供了 切面。切面對關(guān)注點(diǎn)進(jìn)行模塊化,例如橫切多個(gè)類(lèi)型和對象的事務(wù)管理。 (這些關(guān)注點(diǎn)術(shù)語(yǔ)通常稱(chēng)作 橫切(crosscutting) 關(guān)注點(diǎn)。)

Spring的一個(gè)關(guān)鍵的組件就是 AOP框架。 盡管如此,Spring IoC容器并不依賴(lài)于A(yíng)OP,這意味著(zhù)你可以自由選擇是否使用AOP,AOP提供強大的中間件解決方案,這使得Spring IoC容器更加完善。

Spring中所使用的AOP:

  • 提供聲明式企業(yè)服務(wù),特別是為了替代EJB聲明式服務(wù)。 最重要的服務(wù)是 聲明性事務(wù)管理(declarative transaction management) , 這個(gè)服務(wù)建立在Spring的抽象事務(wù)管理(transaction abstraction)之上。

  • 允許用戶(hù)實(shí)現自定義的切面,用AOP來(lái)完善OOP的使用。

這樣你可以把Spring AOP看作是對Spring的一種增強,它使得Spring可以不需要EJB就能提供聲明式事務(wù)管理; 或者也可以使用Spring AOP框架的全部功能來(lái)實(shí)現自定義的切面。

本章首先 介紹了AOP的概念,無(wú)論你打算采用哪種風(fēng)格的切面聲明,這個(gè)部分都值得你一讀。 本章剩下的部分將著(zhù)重于Spring 2.0對AOP的支持; 下一章 提供了關(guān)于Spring 1.2風(fēng)格的AOP概述,也許你已經(jīng)在其他書(shū)本,文章以及已有的應用程序中碰到過(guò)這種AOP風(fēng)格。

如果你只打算使用通用的聲明式服務(wù)或者預先打包的聲明式中間件服務(wù),例如緩沖池(pooling), 那么你不必直接使用Spring AOP,而本章的大部分內容也可以直接跳過(guò)。

6.1.1. AOP概念

首先讓我們從定義一些重要的AOP概念開(kāi)始。這些術(shù)語(yǔ)不是Spring特有的。 不幸的是,AOP術(shù)語(yǔ)并不是特別的直觀(guān);如果Spring使用自己的術(shù)語(yǔ),將會(huì )變得更加令人困惑。

  • 切面(Aspect): 一個(gè)關(guān)注點(diǎn)的模塊化,這個(gè)關(guān)注點(diǎn)可能會(huì )橫切多個(gè)對象。事務(wù)管理是J2EE應用中一個(gè)關(guān)于橫切關(guān)注點(diǎn)的很好的例子。 在Spring AOP中,切面可以使用通用類(lèi)(基于模式的風(fēng)格) 或者在普通類(lèi)中以 @Aspect 注解(@AspectJ風(fēng)格)來(lái)實(shí)現。

  • 連接點(diǎn)(Joinpoint): 在程序執行過(guò)程中某個(gè)特定的點(diǎn),比如某方法調用的時(shí)候或者處理異常的時(shí)候。 在Spring AOP中,一個(gè)連接點(diǎn) 總是 代表一個(gè)方法的執行。 通過(guò)聲明一個(gè)org.aspectj.lang.JoinPoint類(lèi)型的參數可以使通知(Advice)的主體部分獲得連接點(diǎn)信息。

  • 通知(Advice): 在切面的某個(gè)特定的連接點(diǎn)(Joinpoint)上執行的動(dòng)作。通知有各種類(lèi)型,其中包括“around”、“before”和“after”等通知。 通知的類(lèi)型將在后面部分進(jìn)行討論。許多AOP框架,包括Spring,都是以攔截器做通知模型, 并維護一個(gè)以連接點(diǎn)為中心的攔截器鏈。

  • 切入點(diǎn)(Pointcut): 匹配連接點(diǎn)(Joinpoint)的斷言。通知和一個(gè)切入點(diǎn)表達式關(guān)聯(lián),并在滿(mǎn)足這個(gè)切入點(diǎn)的連接點(diǎn)上運行(例如,當執行某個(gè)特定名稱(chēng)的方法時(shí))。 切入點(diǎn)表達式如何和連接點(diǎn)匹配是AOP的核心:Spring缺省使用AspectJ切入點(diǎn)語(yǔ)法。

  • 引入(Introduction): (也被稱(chēng)為內部類(lèi)型聲明(inter-type declaration))。聲明額外的方法或者某個(gè)類(lèi)型的字段。 Spring允許引入新的接口(以及一個(gè)對應的實(shí)現)到任何被代理的對象。 例如,你可以使用一個(gè)引入來(lái)使bean實(shí)現 IsModified 接口,以便簡(jiǎn)化緩存機制。

  • 目標對象(Target Object): 被一個(gè)或者多個(gè)切面(aspect)所通知(advise)的對象。也有人把它叫做 被通知(advised) 對象。 既然Spring AOP是通過(guò)運行時(shí)代理實(shí)現的,這個(gè)對象永遠是一個(gè) 被代理(proxied) 對象。

  • AOP代理(AOP Proxy): AOP框架創(chuàng )建的對象,用來(lái)實(shí)現切面契約(aspect contract)(包括通知方法執行等功能)。 在Spring中,AOP代理可以是JDK動(dòng)態(tài)代理或者CGLIB代理。 注意:Spring 2.0最新引入的基于模式(schema-based)風(fēng)格和@AspectJ注解風(fēng)格的切面聲明,對于使用這些風(fēng)格的用戶(hù)來(lái)說(shuō),代理的創(chuàng )建是透明的。

  • 織入(Weaving): 把切面(aspect)連接到其它的應用程序類(lèi)型或者對象上,并創(chuàng )建一個(gè)被通知(advised)的對象。 這些可以在編譯時(shí)(例如使用AspectJ編譯器),類(lèi)加載時(shí)和運行時(shí)完成。 Spring和其他純Java AOP框架一樣,在運行時(shí)完成織入。

通知的類(lèi)型:

  • 前置通知(Before advice): 在某連接點(diǎn)(join point)之前執行的通知,但這個(gè)通知不能阻止連接點(diǎn)前的執行(除非它拋出一個(gè)異常)。

  • 返回后通知(After returning advice): 在某連接點(diǎn)(join point)正常完成后執行的通知:例如,一個(gè)方法沒(méi)有拋出任何異常,正常返回。

  • 拋出異常后通知(After throwing advice): 在方法拋出異常退出時(shí)執行的通知。

  • 后通知(After (finally) advice): 當某連接點(diǎn)退出的時(shí)候執行的通知(不論是正常返回還是異常退出)。

  • 環(huán)繞通知(Around Advice): 包圍一個(gè)連接點(diǎn)(join point)的通知,如方法調用。這是最強大的一種通知類(lèi)型。 環(huán)繞通知可以在方法調用前后完成自定義的行為。它也會(huì )選擇是否繼續執行連接點(diǎn)或直接返回它們自己的返回值或拋出異常來(lái)結束執行。

環(huán)繞通知是最常用的一種通知類(lèi)型。大部分基于攔截的AOP框架,例如Nanning和JBoss4,都只提供環(huán)繞通知。

跟AspectJ一樣,Spring提供所有類(lèi)型的通知,我們推薦你使用盡量簡(jiǎn)單的通知類(lèi)型來(lái)實(shí)現需要的功能。 例如,如果你只是需要用一個(gè)方法的返回值來(lái)更新緩存,雖然使用環(huán)繞通知也能完成同樣的事情, 但是你最好使用After returning通知而不是環(huán)繞通知。 用最合適的通知類(lèi)型可以使得編程模型變得簡(jiǎn)單,并且能夠避免很多潛在的錯誤。 比如,你不需要調用 JoinPoint(用于A(yíng)round Advice)的 proceed() 方法,就不會(huì )有調用的問(wèn)題。

在Spring 2.0中,所有的通知參數都是靜態(tài)類(lèi)型,因此你可以使用合適的類(lèi)型(例如一個(gè)方法執行后的返回值類(lèi)型)作為通知的參數而不是使用一個(gè)對象數組。

切入點(diǎn)(pointcut)和連接點(diǎn)(join point)匹配的概念是AOP的關(guān)鍵,這使得AOP不同于其它僅僅提供攔截功能的舊技術(shù)。 切入點(diǎn)使得定位通知(advice)可獨立于OO層次。 例如,一個(gè)提供聲明式事務(wù)管理的around通知可以被應用到一組橫跨多個(gè)對象中的方法上(例如服務(wù)層的所有業(yè)務(wù)操作)。

6.1.2. Spring AOP的功能和目標

Spring AOP用純Java實(shí)現。它不需要專(zhuān)門(mén)的編譯過(guò)程。Spring AOP不需要控制類(lèi)裝載器層次,因此它適用于J2EE web容器或應用服務(wù)器。

Spring目前僅支持使用方法調用作為連接點(diǎn)(join point)(在Spring bean上通知方法的執行)。 雖然可以在不影響到Spring AOP核心API的情況下加入對成員變量攔截器支持,但Spring并沒(méi)有實(shí)現成員變量攔截器。 如果你需要把對成員變量的訪(fǎng)問(wèn)和更新也作為通知的連接點(diǎn),可以考慮其它語(yǔ)法的Java語(yǔ)言,例如AspectJ。

Spring實(shí)現AOP的方法跟其他的框架不同。Spring并不是要嘗試提供最完整的AOP實(shí)現(盡管Spring AOP有這個(gè)能力), 相反的,它其實(shí)側重于提供一種AOP實(shí)現和Spring IoC容器的整合,用于幫助解決在企業(yè)級開(kāi)發(fā)中的常見(jiàn)問(wèn)題。

因此,Spring AOP通常都和Spring IoC容器一起使用。 Aspect使用普通的bean定義語(yǔ)法(盡管Spring提供了強大的“自動(dòng)代理(autoproxying)”功能): 與其他AOP實(shí)現相比這是一個(gè)顯著(zhù)的區別。有些事使用Spring AOP是無(wú)法輕松或者高效的完成的,比如說(shuō)通知一個(gè)細粒度的對象。 這種時(shí)候,使用AspectJ是最好的選擇。不過(guò)經(jīng)驗告訴我們: 于大多數在J2EE應用中遇到的問(wèn)題,只要適合AOP來(lái)解決的,Spring AOP都沒(méi)有問(wèn)題,Spring AOP提供了一個(gè)非常好的解決方案。

Spring AOP從來(lái)沒(méi)有打算通過(guò)提供一種全面的AOP解決方案來(lái)取代AspectJ。 我們相信無(wú)論是基于代理(proxy-based )的框架比如說(shuō)Spring亦或是full-blown的框架比如說(shuō)是AspectJ都是很有價(jià)值的,他們之間的關(guān)系應該是互補而不是競爭的關(guān)系。 Spring 2.0可以無(wú)縫的整合Spring AOP,IoC 和AspectJ,使得所有的AOP應用完全融入基于Spring的應用體系。 這樣的集成不會(huì )影響Spring AOP API或者AOP Alliance API;Spring AOP保留了向下兼容性。接下來(lái)的一章會(huì )詳細討論Spring AOP API。

6.1.3. Spring的AOP代理

Spring缺省使用J2SE 動(dòng)態(tài)代理(dynamic proxies)來(lái)作為AOP的代理。這樣任何接口都可以被代理。

Spring也支持使用CGLIB代理. 對于需要代理類(lèi)而不是代理接口的時(shí)候CGLIB代理是很有必要的。 如果一個(gè)業(yè)務(wù)對象并沒(méi)有實(shí)現一個(gè)接口,默認就會(huì )使用CGLIB。 作為面向接口編程的最佳實(shí)踐,業(yè)務(wù)對象通常都會(huì )實(shí)現一個(gè)或多個(gè)接口。但也有可能會(huì ) 強制使用CGLIB, 在這種情況(希望不常有)下,你可能需要通知一個(gè)沒(méi)有在接口中聲明的方法,或者需要傳入一個(gè)代理對象給方法作為具體類(lèi)型

在Spring 2.0之后,Spring可能會(huì )提供多種其他類(lèi)型的AOP代理,包括了完整的生成類(lèi)。這不會(huì )影響到編程模型。

6.2. @AspectJ支持

"@AspectJ"使用了Java 5的注解,可以將切面聲明為普通的Java類(lèi)。 AspectJ 5發(fā)布的 AspectJ project 中引入了這種@AspectJ風(fēng)格。 Spring 2.0 使用了和AspectJ 5一樣的注解,使用了AspectJ 提供的一個(gè)庫來(lái)做切點(diǎn)(pointcut)解析和匹配。 但是,AOP在運行時(shí)仍舊是純的Spring AOP,并不依賴(lài)于A(yíng)spectJ 的編譯器或者織入器(weaver)。

使用AspectJ的編譯器或者織入器(weaver)的話(huà)就可以使用完整的AspectJ 語(yǔ)言,我們將在 Section 6.8, “在Spring應用中使用AspectJ” 中討論這個(gè)問(wèn)題。

6.2.1. 啟用@AspectJ支持

為了在Spring配置中使用@AspectJ aspects,你必須首先啟用Spring對基于@AspectJ aspects的配置支持,自動(dòng)代理(autoproxying)基于通知是否來(lái)自這些切面。 自動(dòng)代理是指Spring會(huì )判斷一個(gè)bean是否使用了一個(gè)或多個(gè)切面通知,并據此自動(dòng)生成相應的代理以攔截其方法調用,并且確認通知是否如期進(jìn)行。

通過(guò)在你的Spring的配置中引入下列元素來(lái)啟用Spring對@AspectJ的支持:

<aop:aspectj-autoproxy/>

我們假使你正在使用 Appendix A, XML Schema-based configuration 所描述的schema支持。 關(guān)于如何在aop的命名空間中引入這些標簽,請參見(jiàn) Section A.2.6, “The aop schema”

如果你正在使用DTD,你仍舊可以通過(guò)在你的application context中添加如下定義來(lái)啟用@AspectJ支持:

<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />

你需要在你的應用程序的classpath中引入兩個(gè)AspectJ庫:aspectjweaver.jaraspectjrt.jar。 這些庫可以在A(yíng)spectJ的安裝包(1.5.1或者之后的版本)中的 lib 目錄里找到,或者也可以在Spring依賴(lài)庫的 lib/aspectj 目錄下找到。

6.2.2. 聲明一個(gè)切面

在啟用@AspectJ支持的情況下,在application context中定義的任意帶有一個(gè)@Aspect切面(擁有@Aspect注解)的bean都將被Spring自動(dòng)識別并用于配置在Spring AOP。 以下例子展示了為了完成一個(gè)不是非常有用的切面所需要的最小定義:

下面是在application context中的一個(gè)常見(jiàn)的bean定義,這個(gè)bean指向一個(gè)使用了 @Aspect 注解的bean類(lèi):

<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect"><!-- configure properties of aspect here as normal --></bean>

下面是 NotVeryUsefulAspect 類(lèi)定義,使用了 org.aspectj.lang.annotation.Aspect 注解。

package org.xyz;import org.aspectj.lang.annotation.Aspect;@Aspectpublic class NotVeryUsefulAspect {}

切面(用 @Aspect 注解的類(lèi))和其他類(lèi)一樣有方法和字段定義。他們也可能包括切入點(diǎn),通知和引入(inter-type)聲明。

6.2.3. 聲明一個(gè)切入點(diǎn)(pointcut)

回想一下,切入點(diǎn)決定了連接點(diǎn)關(guān)注的內容,使得我們可以控制通知什么時(shí)候執行。 Spring AOP 只支持 Spring bean 方法執行連接點(diǎn)。所以你可以把切入點(diǎn)看做是匹配 Spring bean 上方法的執行。 一個(gè)切入點(diǎn)聲明有兩個(gè)部分:一個(gè)包含名字和任意參數的簽名,還有一個(gè)切入點(diǎn)表達式,該表達式?jīng)Q定了我們關(guān)注那個(gè)方法的執行。 在 @AspectJ 注解風(fēng)格的 AOP 中,一個(gè)切入點(diǎn)簽名通過(guò)一個(gè)普通的方法定義來(lái)提供,并且切入點(diǎn)表達式使用 @Pointcut 注解來(lái)表示(作為切入點(diǎn)簽名的方法必須返回 void 類(lèi)型)。

用一個(gè)例子會(huì )幫助我們區分切入點(diǎn)簽名和切入點(diǎn)表達式之間的差別,下面的例子定義了一個(gè)切入點(diǎn)‘a(chǎn)nyOldTransfer‘, 這個(gè)切入點(diǎn)將匹配任何名為 "transfer" 的方法的執行:

@Pointcut("execution(* transfer(..))")// the pointcut expressionprivate void anyOldTransfer() {}// the pointcut signature

切入點(diǎn)表達式,也就是 @Pointcut 注解的值,是正規的AspectJ 5切入點(diǎn)表達式。 如果你想要更多了解AspectJ的 切入點(diǎn)語(yǔ)言,請參見(jiàn) AspectJ 編程指南(如果要了解基于Java 5的擴展請參閱 AspectJ 5 開(kāi)發(fā)手冊) 或者其他人寫(xiě)的關(guān)于A(yíng)spectJ的書(shū),例如Colyer et. al.著(zhù)的《Eclipse AspectJ》或者Ramnivas Laddad著(zhù)的《AspectJ in Action》。

6.2.3.1. 切入點(diǎn)指定者的支持

Spring AOP 支持在切入點(diǎn)表達式中使用如下的AspectJ切入點(diǎn)指定者:

  • execution - 匹配方法執行的連接點(diǎn),這是你將會(huì )用到的Spring的最主要的切入點(diǎn)指定者。

  • within - 限定匹配特定類(lèi)型的連接點(diǎn)(在使用Spring AOP的時(shí)候,在匹配的類(lèi)型中定義的方法的執行)。

  • this - 限定匹配特定的連接點(diǎn)(使用Spring AOP的時(shí)候方法的執行),其中bean reference(Spring AOP 代理)是指定類(lèi)型的實(shí)例。

  • target - 限定匹配特定的連接點(diǎn)(使用Spring AOP的時(shí)候方法的執行),其中目標對象(被代理的appolication object)是指定類(lèi)型的實(shí)例。

  • args - 限定匹配特定的連接點(diǎn)(使用Spring AOP的時(shí)候方法的執行),其中參數是指定類(lèi)型的實(shí)例。

  • @target - 限定匹配特定的連接點(diǎn)(使用Spring AOP的時(shí)候方法的執行),其中執行的對象的類(lèi)已經(jīng)有指定類(lèi)型的注解。

  • @args - 限定匹配特定的連接點(diǎn)(使用Spring AOP的時(shí)候方法的執行),其中實(shí)際傳入參數的運行時(shí)類(lèi)型有指定類(lèi)型的注解。

  • @within - 限定匹配特定的連接點(diǎn),其中連接點(diǎn)所在類(lèi)型已指定注解(在使用Spring AOP的時(shí)候,所執行的方法所在類(lèi)型已指定注解)。

  • @annotation - 限定匹配特定的連接點(diǎn)(使用Spring AOP的時(shí)候方法的執行),其中連接點(diǎn)的主題有某種給定的注解。

因為Spring AOP限制了連接點(diǎn)必須是方法執行級別的,pointcut designators的討論也給出了一個(gè)定義,這個(gè)定義和AspectJ的編程指南中的定義相比顯得更加狹窄。 除此之外,AspectJ它本身有基于類(lèi)型的語(yǔ)義,在執行的連接點(diǎn)‘this‘和‘target‘都是指同一個(gè)對象,也就是執行方法的對象。 Spring AOP是一個(gè)基于代理的系統,并且嚴格區分代理對象本身(對應于‘this‘)和背后的目標對象(對應于‘target‘)

6.2.3.2. 合并切入點(diǎn)表達式

切入點(diǎn)表達式可以使用using ‘&‘, ‘||‘ 和 ‘!‘來(lái)合并.還可以通過(guò)名字來(lái)指向切入點(diǎn)表達式。 以下的例子展示了三種切入點(diǎn)表達式: anyPublicOperation(在一個(gè)方法執行連接點(diǎn)代表了任意public方法的執行時(shí)匹配); inTrading(在一個(gè)代表了在交易模塊中的任意的方法執行時(shí)匹配) 和 tradingOperation(在一個(gè)代表了在交易模塊中的任意的公共方法執行時(shí)匹配)。

	@Pointcut("execution(public * *(..))")private void anyPublicOperation() {}@Pointcut("within(com.xyz.someapp.trading..*")private void inTrading() {}@Pointcut("anyPublicOperation() && inTrading()")private void tradingOperation() {}

就上所示的,從更小的命名組件來(lái)構建更加復雜的切入點(diǎn)表達式是一種最佳實(shí)踐。 當用名字來(lái)指定切入點(diǎn)時(shí)使用的是常見(jiàn)的Java成員可視性訪(fǎng)問(wèn)規則。 (比如說(shuō),你可以在同一類(lèi)型中訪(fǎng)問(wèn)私有的切入點(diǎn),在繼承關(guān)系中訪(fǎng)問(wèn)受保護的切入點(diǎn),可以在任意地方訪(fǎng)問(wèn)公共切入點(diǎn)。 成員可視性訪(fǎng)問(wèn)規則不影響到切入點(diǎn)的 匹配。

6.2.3.3. 共享常見(jiàn)的切入點(diǎn)(pointcut)定義

當開(kāi)發(fā)企業(yè)級應用的時(shí)候,你通常會(huì )想要從幾個(gè)切面來(lái)參考模塊化的應用和特定操作的集合。 我們推薦定義一個(gè)“SystemArchitecture”切面來(lái)捕捉常見(jiàn)的切入點(diǎn)表達式。一個(gè)典型的切面可能看起來(lái)像下面這樣:

package com.xyz.someapp;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;@Aspectpublic class SystemArchitecture {/*** A join point is in the web layer if the method is defined* in a type in the com.xyz.someapp.web package or any sub-package* under that.*/@Pointcut("within(com.xyz.someapp.web..*)")public void inWebLayer() {}/*** A join point is in the service layer if the method is defined* in a type in the com.xyz.someapp.service package or any sub-package* under that.*/@Pointcut("within(com.xyz.someapp.service..*)")public void inServiceLayer() {}/*** A join point is in the data access layer if the method is defined* in a type in the com.xyz.someapp.dao package or any sub-package* under that.*/@Pointcut("within(com.xyz.someapp.dao..*)")public void inDataAccessLayer() {}/*** A business service is the execution of any method defined on a service* interface. This definition assumes that interfaces are placed in the* "service" package, and that implementation types are in sub-packages.** If you group service interfaces by functional area (for example,* in packages com.xyz.someapp.abc.service and com.xyz.def.service) then* the pointcut expression "execution(* com.xyz.someapp..service.*.*(..))"* could be used instead.*/@Pointcut("execution(* com.xyz.someapp.service.*.*(..))")public void businessService() {}/*** A data access operation is the execution of any method defined on a* dao interface. This definition assumes that interfaces are placed in the* "dao" package, and that implementation types are in sub-packages.*/@Pointcut("execution(* com.xyz.someapp.dao.*.*(..))")public void dataAccessOperation() {}}

示例中的切入點(diǎn)定義了一個(gè)你可以在任何需要切入點(diǎn)表達式的地方可引用的切面。比如,為了使service層事務(wù)化,你可以寫(xiě)成:

<aop:config><aop:advisorpointcut="com.xyz.someapp.SystemArchitecture.businessService()"advice-ref="tx-advice"/></aop:config><tx:advice id="tx-advice"><tx:attributes><tx:method name="*" propagation="REQUIRED"/></tx:attributes></tx:advice>

Section 6.3, “Schema-based AOP support” 中討論 <aop:config><aop:advisor>標簽。 在 Chapter 9, 事務(wù)管理 中討論事務(wù)標簽。

6.2.3.4. 示例

Spring AOP 用戶(hù)可能會(huì )經(jīng)常使用 execution pointcut designator。執行表達式的格式如下:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

除了返回類(lèi)型模式(上面代碼片斷中的ret-type-pattern),名字模式和參數模式以外,所有的部分都是可選的。 返回類(lèi)型模式?jīng)Q定了方法的返回類(lèi)型必須依次匹配一個(gè)連接點(diǎn)。 你會(huì )使用的最頻繁的返回類(lèi)型模式是 *,它代表了匹配任意的返回類(lèi)型。 一個(gè)全稱(chēng)限定的類(lèi)型名將只會(huì )匹配返回給定類(lèi)型的方法。名字模式匹配的是方法名。 你可以使用 * 通配符作為所有或者部分命名模式。 參數模式稍微有點(diǎn)復雜:() 匹配了一個(gè)不接受任何參數的方法, 而 (..) 匹配了一個(gè)接受任意數量參數的方法(零或者更多)。 模式 (*) 匹配了一個(gè)接受一個(gè)任何類(lèi)型的參數的方法。 模式 (*,String) 匹配了一個(gè)接受兩個(gè)參數的方法,第一個(gè)可以是任意類(lèi)型,第二個(gè)則必須是String類(lèi)型。 請參見(jiàn)AspectJ編程指南的 Language Semantics 部分。

下面給出一些常見(jiàn)切入點(diǎn)表達式的例子。

  • 任意公共方法的執行:

    execution(public * *(..))
  • 任何一個(gè)以“set”開(kāi)始的方法的執行:

    execution(* set*(..))
  • AccountService 接口的任意方法的執行:

    execution(* com.xyz.service.AccountService.*(..))
  • 定義在service包里的任意方法的執行:

    execution(* com.xyz.service.*.*(..))
  • 定義在service包或者子包里的任意方法的執行:

    execution(* com.xyz.service..*.*(..))
  • 在service包里的任意連接點(diǎn)(在Spring AOP中只是方法執行) :

    within(com.xyz.service.*)
  • 在service包或者子包里的任意連接點(diǎn)(在Spring AOP中只是方法執行) :

    within(com.xyz.service..*)
  • 實(shí)現了 AccountService 接口的代理對象的任意連接點(diǎn)(在Spring AOP中只是方法執行) :

    this(com.xyz.service.AccountService)
    ‘this‘在binding form中用的更多:- 請常見(jiàn)以下討論通知的章節中關(guān)于如何使得代理對象可以在通知體內訪(fǎng)問(wèn)到的部分。
  • 實(shí)現了 AccountService 接口的目標對象的任意連接點(diǎn)(在Spring AOP中只是方法執行) :

    target(com.xyz.service.AccountService)
    ‘target‘在binding form中用的更多:- 請常見(jiàn)以下討論通知的章節中關(guān)于如何使得目標對象可以在通知體內訪(fǎng)問(wèn)到的部分。
  • 任何一個(gè)只接受一個(gè)參數,且在運行時(shí)傳入的參數實(shí)現了 Serializable 接口的連接點(diǎn) (在Spring AOP中只是方法執行)

    args(java.io.Serializable)
    ‘a(chǎn)rgs‘在binding form中用的更多:- 請常見(jiàn)以下討論通知的章節中關(guān)于如何使得方法參數可以在通知體內訪(fǎng)問(wèn)到的部分。

    請注意在例子中給出的切入點(diǎn)不同于 execution(* *(java.io.Serializable)): args只有在動(dòng)態(tài)運行時(shí)候傳入參數是可序列化的(Serializable)才匹配,而execution 在傳入參數的簽名聲明的類(lèi)型實(shí)現了 Serializable 接口時(shí)候匹配。

  • 有一個(gè) @Transactional 注解的目標對象中的任意連接點(diǎn)(在Spring AOP中只是方法執行)

    @target(org.springframework.transaction.annotation.Transactional)
    ‘@target‘ 也可以在binding form中使用:請常見(jiàn)以下討論通知的章節中關(guān)于如何使得annotation對象可以在通知體內訪(fǎng)問(wèn)到的部分。
  • 任何一個(gè)目標對象聲明的類(lèi)型有一個(gè) @Transactional 注解的連接點(diǎn)(在Spring AOP中只是方法執行)

    @within(org.springframework.transaction.annotation.Transactional)
    ‘@within‘也可以在binding form中使用:- 請常見(jiàn)以下討論通知的章節中關(guān)于如何使得annotation對象可以在通知體內訪(fǎng)問(wèn)到的部分。
  • 任何一個(gè)執行的方法有一個(gè) @Transactional annotation的連接點(diǎn)(在Spring AOP中只是方法執行)

    @annotation(org.springframework.transaction.annotation.Transactional)
    ‘@annotation‘ 也可以在binding form中使用:- 請常見(jiàn)以下討論通知的章節中關(guān)于如何使得annotation對象可以在通知體內訪(fǎng)問(wèn)到的部分。
  • 任何一個(gè)接受一個(gè)參數,并且傳入的參數在運行時(shí)的類(lèi)型實(shí)現了 @Classified annotation的連接點(diǎn)(在Spring AOP中只是方法執行)

    @args(com.xyz.security.Classified)
    ‘@args‘也可以在binding form中使用:- 請常見(jiàn)以下討論通知的章節中關(guān)于如何使得annotation對象可以在通知體內訪(fǎng)問(wèn)到的部分。

6.2.4. 聲明通知

通知是跟一個(gè)切入點(diǎn)表達式關(guān)聯(lián)起來(lái)的,并且在切入點(diǎn)匹配的方法執行之前或者之后或者之前和之后運行。 切入點(diǎn)表達式可能是指向已命名的切入點(diǎn)的簡(jiǎn)單引用或者是一個(gè)已經(jīng)聲明過(guò)的切入點(diǎn)表達式。

6.2.4.1. 前置通知(Before advice)

一個(gè)切面里使用 @Before 注解聲明前置通知:

import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspectpublic class BeforeExample {@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")public void doAccessCheck() {// ...}}

如果使用一個(gè)in-place 的切入點(diǎn)表達式,我們可以把上面的例子換個(gè)寫(xiě)法:

import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspectpublic class BeforeExample {@Before("execution(* com.xyz.myapp.dao.*.*(..))")public void doAccessCheck() {// ...}}

6.2.4.2. 返回后通知(After returning advice)

返回后通知通常在一個(gè)匹配的方法返回的時(shí)候執行。使用 @AfterReturning 注解來(lái)聲明:

import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.AfterReturning;@Aspectpublic class AfterReturningExample {@AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")public void doAccessCheck() {// ...}}
說(shuō)明:你可以在同一個(gè)切面里定義多個(gè)通知,或者其他成員。我們只是在展示如何定義一個(gè)簡(jiǎn)單的通知。這些例子主要的側重點(diǎn)是正在討論的問(wèn)題。

有時(shí)候你需要在通知體內得到返回的值。你可以使用以 @AfterReturning 接口的形式來(lái)綁定返回值:

import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.AfterReturning;@Aspectpublic class AfterReturningExample {@AfterReturning(pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",returning="retVal")public void doAccessCheck(Object retVal) {// ...}}

returning 屬性中使用的名字必須對應于通知方法內的一個(gè)參數名。 當一個(gè)方法執行返回后,返回值作為相應的參數值傳入通知方法。 一個(gè) returning 子句也限制了只能匹配到返回指定類(lèi)型值的方法。 (在本例子中,返回值是 Object 類(lèi),也就是說(shuō)返回任意類(lèi)型都會(huì )匹配)

6.2.4.3. 拋出后通知(After throwing advice)

拋出后通知在一個(gè)方法拋出異常后執行。使用 @AfterThrowing 注解來(lái)聲明:

import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.AfterThrowing;@Aspectpublic class AfterThrowingExample {@AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")public void doRecoveryActions() {// ...}}

你通常會(huì )想要限制通知只在某種特殊的異常被拋出的時(shí)候匹配,你還希望可以在通知體內得到被拋出的異常。 使用 throwing 屬性不光可以限制匹配的異常類(lèi)型(如果你不想限制,請使用 Throwable 作為異常類(lèi)型),還可以將拋出的異常綁定到通知的一個(gè)參數上。

import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.AfterThrowing;@Aspectpublic class AfterThrowingExample {@AfterThrowing(pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",throwing="ex")public void doRecoveryActions(DataAccessException ex) {// ...}}

throwing 屬性中使用的名字必須與通知方法內的一個(gè)參數對應。 當一個(gè)方法因拋出一個(gè)異常而中止后,這個(gè)異常將會(huì )作為那個(gè)對應的參數送至通知方法。 throwing 子句也限制了只能匹配到拋出指定異常類(lèi)型的方法(上面的示例為 DataAccessException)。

6.2.4.4. 后通知(After (finally) advice)

不論一個(gè)方法是如何結束的,在它結束后(finally)后通知(After (finally) advice)都會(huì )運行。 使用 @After 注解來(lái)聲明。這個(gè)通知必須做好處理正常返回和異常返回兩種情況。通常用來(lái)釋放資源。

import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.After;@Aspectpublic class AfterFinallyExample {@After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")public void doReleaseLock() {// ...}}

6.2.4.5. 環(huán)繞通知(Around Advice)

最后一種通知是環(huán)繞通知。環(huán)繞通知在一個(gè)方法執行之前和之后執行。 它使得通知有機會(huì )既在一個(gè)方法執行之前又在執行之后運行。并且,它可以決定這個(gè)方法在什么時(shí)候執行,如何執行,甚至是否執行。 環(huán)繞通知經(jīng)常在在某線(xiàn)程安全的環(huán)境下,你需要在一個(gè)方法執行之前和之后共享某種狀態(tài)的時(shí)候使用。 請盡量使用最簡(jiǎn)單的滿(mǎn)足你需求的通知。(比如如果前置通知(before advice)也可以適用的情況下不要使用環(huán)繞通知)。

環(huán)繞通知使用 @Around 注解來(lái)聲明。通知的第一個(gè)參數必須是 ProceedingJoinPoint 類(lèi)型。 在通知體內,調用 ProceedingJoinPointproceed() 方法將會(huì )導致潛在的連接點(diǎn)方法執行。 proceed 方法也可能會(huì )被調用并且傳入一個(gè) Object[] 對象-該數組將作為方法執行時(shí)候的參數。

當傳入一個(gè) Object[] 對象的時(shí)候,處理的方法與通過(guò)AspectJ編譯器處理環(huán)繞通知略有不同。 對于使用傳統AspectJ語(yǔ)言寫(xiě)的環(huán)繞通知來(lái)說(shuō),傳入參數的數量必須和傳遞給環(huán)繞通知的參數數量匹配(不是后臺的連接點(diǎn)接受的參數數量),并且特定順序的傳入參數代替了將要綁定給連接點(diǎn)的原始值(如果你看不懂不用擔心)。 Spring采用的方法更加簡(jiǎn)單并且更好得和他的基于代理(proxy-based),只匹配執行的語(yǔ)法相適用。 如果你適用AspectJ的編譯器和編織器來(lái)編譯為Spring而寫(xiě)的@AspectJ切面和處理參數,你只需要了解這一區別即可。 有一種方法可以讓你寫(xiě)出100%兼容Spring AOP和AspectJ的,我們將會(huì )在后續的通知參數(advice parameters)的章節中討論它。
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.ProceedingJoinPoint;@Aspectpublic class AroundExample {@Around("com.xyz.myapp.SystemArchitecture.businessService()")public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {// start stopwatchObject retVal = pjp.proceed();// stop stopwatchreturn retVal;}}

方法的調用者得到的返回值就是環(huán)繞通知返回的值。 例如:一個(gè)簡(jiǎn)單的緩存切面,如果緩存中有值,就返回該值,否則調用proceed()方法。 請注意proceed可能在通知體內部被調用一次,許多次,或者根本不被調用。

6.2.4.6. 通知參數(Advice parameters)

Spring 2.0 提供了完整的通知類(lèi)型 - 這意味著(zhù)你可以在通知簽名中聲明所需的參數,(就像在以前的例子中我們看到的返回值和拋出異常一樣)而不總是使用Object[]。 我們將會(huì )看到如何在通知體內訪(fǎng)問(wèn)參數和其他上下文相關(guān)的值。首先讓我們看以下如何編寫(xiě)普通的通知以找出正在被通知的方法。

6.2.4.6.1. 訪(fǎng)問(wèn)當前的連接點(diǎn)

任何通知方法可以將第一個(gè)參數定義為 org.aspectj.lang.JoinPoint 類(lèi)型 (環(huán)繞通知需要定義為 ProceedingJoinPoint 類(lèi)型的, 它是 JoinPoint 的一個(gè)子類(lèi)。) JoinPoint 接口提供了一系列有用的方法, 比如 getArgs()(返回方法參數)、getThis()(返回代理對象)、getTarget()(返回目標)、getSignature()(返回正在被通知的方法相關(guān)信息)和 toString()(打印出正在被通知的方法的有用信息)。詳細的內容請參考Javadocs。

6.2.4.6.2. 傳遞參數給通知(Advice)

我們已經(jīng)看到了如何綁定返回值或者異常(使用后置通知(after returning)和異常后通知(after throwing advice)。 為了可以在通知(adivce)體內訪(fǎng)問(wèn)參數,你可以使用 args 來(lái)綁定。 如果在一個(gè)參數表達式中應該使用類(lèi)型名字的地方使用一個(gè)參數名字,那么當通知執行的時(shí)候對應的參數值將會(huì )被傳遞進(jìn)來(lái)。 可能給出一個(gè)例子會(huì )更好理解。假使你想要通知(advise)接受某個(gè)Account對象作為第一個(gè)參數的DAO操作的執行,你想要在通知體內也能訪(fǎng)問(wèn)到account對象,你可以寫(xiě)如下的代碼:

@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation() &&" +"args(account,..)")public void validateAccount(Account account) {// ...}

切入點(diǎn)表達式的 args(account,..) 部分有兩個(gè)目的: 首先它保證了只會(huì )匹配那些接受至少一個(gè)參數的方法的執行,而且傳入的參數必須是 Account 類(lèi)型的實(shí)例, 其次它使得可以在通知體內通過(guò) account 參數來(lái)訪(fǎng)問(wèn)那個(gè)account參數。

另外一個(gè)辦法是定義一個(gè)切入點(diǎn),這個(gè)切入點(diǎn)在匹配某個(gè)連接點(diǎn)的時(shí)候“提供”了一個(gè)Account對象, 然后直接從通知中訪(fǎng)問(wèn)那個(gè)命名的切入點(diǎn)。你可以這樣寫(xiě):

@Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() &&" +"args(account,..)")private void accountDataAccessOperation(Account account) {}@Before("accountDataAccessOperation(account)")public void validateAccount(Account account) {// ...}

如果有興趣了解更詳細的內容,請參閱 AspectJ 編程指南。

代理對象(this)、目標對象(target) 和注解(@within, @target, @annotation, @args)都可以用一種簡(jiǎn)單格式綁定。 以下的例子展示了如何使用 @Auditable 注解來(lái)匹配方法執行,并提取AuditCode。

首先是 @Auditable 注解的定義:

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface Auditable {AuditCode value();}

然后是匹配 @Auditable 方法執行的通知:

@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && " +"@annotation(auditable)")public void audit(Auditable auditable) {AuditCode code = auditable.value();// ...}
6.2.4.6.3. 決定參數名

綁定在通知上的參數依賴(lài)切入點(diǎn)表達式的匹配名,并借此在(通知(advice)和切入點(diǎn)(pointcut))的方法簽名中聲明參數名。 參數名 無(wú)法 通過(guò)Java反射來(lái)獲取,所以Spring AOP使用如下的策略來(lái)決定參數名字:

  1. 如果參數名字已經(jīng)被用戶(hù)明確指定,則使用指定的參數名: 通知(advice)和切入點(diǎn)(pointcut)注解有一個(gè)額外的"argNames"屬性,該屬性用來(lái)指定所注解的方法的參數名 - 這些參數名在運行時(shí)是 可以 訪(fǎng)問(wèn)的。例子如下:

    @Before(    value="com.xyz.lib.Pointcuts.anyPublicMethod() && " +    "@annotation(auditable)",    argNames="auditable")    public void audit(Auditable auditable) {    AuditCode code = auditable.value();    // ...    }
    如果一個(gè)@AspectJ切面已經(jīng)被AspectJ編譯器(ajc)編譯過(guò)了,那么就不需要再添加 argNames 參數了,因為編譯器會(huì )自動(dòng)完成這一工作。
  2. 使用 ‘a(chǎn)rgNames‘ 屬性有點(diǎn)不那么優(yōu)雅,所以如果沒(méi)有指定‘a(chǎn)rgNames‘ 屬性, Spring AOP 會(huì )尋找類(lèi)的debug信息,并且嘗試從本地變量表(local variable table)中來(lái)決定參數名字。 只要編譯的時(shí)候使用了debug信息(至少要使用 ‘-g:vars‘ ),就可獲得這些信息。 使用這個(gè)flag編譯的結果是: (1)你的代碼將能夠更加容易的讀懂(反向工程), (2)生成的class文件會(huì )稍許大一些(通常是不重要的), (3)移除不被使用的本地變量的優(yōu)化功能將會(huì )失效。 換句話(huà)說(shuō),你在使用這個(gè)flag的時(shí)候不會(huì )遇到任何困難。

  3. 如果不加上debug信息來(lái)編譯的話(huà),Spring AOP將會(huì )嘗試推斷參數的綁定。 (例如,要是只有一個(gè)變量被綁定到切入點(diǎn)表達式(pointcut expression)、通知方法(advice method)將會(huì )接受這個(gè)參數, 這是顯而易見(jiàn)的)。 如果變量的綁定不明確,將會(huì )拋出一個(gè) AmbiguousBindingException 異常。

  4. 如果以上所有策略都失敗了,將會(huì )拋出一個(gè) IllegalArgumentException 異常。

6.2.4.6.4. 處理參數

我們之前提過(guò)我們將會(huì )討論如何編寫(xiě)一個(gè) 帶參數的 的proceed()調用,使得不論在Spring AOP中還是在A(yíng)spectJ都能正常工作。 解決方法是保證通知簽名依次綁定方法參數。比如說(shuō):

@Around("execution(List<Account> find*(..)) &&" +"com.xyz.myapp.SystemArchitecture.inDataAccessLayer() && " +"args(accountHolderNamePattern)")public Object preProcessQueryPattern(ProceedingJoinPoint pjp, String accountHolderNamePattern)throws Throwable {String newPattern = preProcess(accountHolderNamePattern);return pjp.proceed(new Object[] {newPattern});}

大多數情況下你都會(huì )這樣綁定(就像上面的例子那樣)。

6.2.4.7. 通知(Advice)順序

如果有多個(gè)通知想要在同一連接點(diǎn)運行會(huì )發(fā)生什么?Spring AOP 的執行通知的順序跟AspectJ的一樣。 在“進(jìn)入”連接點(diǎn)的情況下,最高優(yōu)先級的通知會(huì )先執行(所以上面給出的兩個(gè)前置通知(before advice)中,優(yōu)先級高的那個(gè)會(huì )先執行)。 在“退出”連接點(diǎn)的情況下,最高優(yōu)先級的通知會(huì )最后執行。(所以上面給出的兩個(gè)前置通知(before advice)中,優(yōu)先級高的那個(gè)會(huì )第二個(gè)執行)。 對于定義在相同切面的通知,根據聲明的順序來(lái)確定執行順序。比如下面這個(gè)切面:

@Aspectpublic class AspectWithMultipleAdviceDeclarations {@Pointcut("execution(* foo(..))")public void fooExecution() {}@Before("fooExecution()")public void doBeforeOne() {// ...}@Before("fooExecution()")public void doBeforeTwo() {// ...}@AfterReturning("fooExecution()")public void doAfterOne() {// ...}@AfterReturning("fooExecution()")public void doAfterTwo() {// ...}}

這樣,假使對于任何一個(gè)名字為foo的方法的執行, doBeforeOne、doBeforeTwo、doAfterOnedoAfterTwo 通知方法都需要運行。 執行順序將按照聲明的順序來(lái)確定。在這個(gè)例子中,執行的結果會(huì )是:

doBeforeOnedoBeforeTwofoodoAfterOnedoAfterTwo

換言之,因為doBeforeOne先定義,它會(huì )先于doBeforeTwo執行,而doAfterTwo后于doAfterOne定義,所以它會(huì )在doAfterOne之后執行。 只需要記住通知是按照定義的順序來(lái)執行的就可以了。 - 如果想要知道更加詳細的內容,請參閱AspectJ編程指南。

當定義在 不同的 切面里的兩個(gè)通知都需要在一個(gè)相同的連接點(diǎn)中運行,那么除非你指定,否則執行的順序是未知的。 你可以通過(guò)指定優(yōu)先級來(lái)控制執行順序。在Spring中可以在切面類(lèi)中實(shí)現 org.springframework.core.Ordered 接口做到這一點(diǎn)。 在兩個(gè)切面中,Ordered.getValue() 方法返回值較低的那個(gè)有更高的優(yōu)先級。

6.2.5. 引入(Introductions)

引入(Introductions)(在A(yíng)spectJ中被稱(chēng)為inter-type聲明)使得一個(gè)切面可以定義被通知對象實(shí)現一個(gè)給定的接口,并且可以代表那些對象提供具體實(shí)現。

使用 @DeclareParents注解來(lái)定義引入。這個(gè)注解被用來(lái)定義匹配的類(lèi)型擁有一個(gè)新的父親。 比如,給定一個(gè)接口 UsageTracked,然后接口的具體實(shí)現 DefaultUsageTracked 類(lèi), 接下來(lái)的切面聲明了所有的service接口的實(shí)現都實(shí)現了 UsageTracked 接口。(比如為了通過(guò)JMX輸出統計信息)。

@Aspectpublic class UsageTracking {@DeclareParents(value="com.xzy.myapp.service.*+",defaultImpl=DefaultUsageTracked.class)public static UsageTracked mixin;@Before("com.xyz.myapp.SystemArchitecture.businessService() &&" +"this(usageTracked)")public void recordUsage(UsageTracked usageTracked) {usageTracked.incrementUseCount();}}

實(shí)現的接口通過(guò)被注解的字段類(lèi)型來(lái)決定。@DeclareParents 注解的 value 屬性是一個(gè)AspectJ的類(lèi)型模式:- 任何匹配類(lèi)型的bean都會(huì )實(shí)現 UsageTracked 接口。 請注意,在上面的前置通知(before advice)的例子中,service beans 可以直接用作 UsageTracked 接口的實(shí)現。 如果需要編程式的來(lái)訪(fǎng)問(wèn)一個(gè)bean,你可以這樣寫(xiě):

UsageTracked usageTracked = (UsageTracked) context.getBean("myService");

6.2.6. 切面實(shí)例化模型

這是一個(gè)高級主題...

默認情況下,在application context中每一個(gè)切面都會(huì )有一個(gè)實(shí)例。 AspectJ 把這個(gè)叫做單個(gè)實(shí)例化模型(singleton instantiation model)。 也可以用其他的生命周期來(lái)定義切面:- Spring支持AspectJ的 perthispertarget 實(shí)例化模型 (現在還不支持percflow、percflowbelowpertypewithin )。

一個(gè)"perthis" 切面的定義:在 @Aspect 注解中指定perthis 子句。 讓我們先來(lái)看一個(gè)例子,然后解釋它是如何運作的:

@Aspect("perthis(com.xyz.myapp.SystemArchitecture.businessService())")public class MyAspect {private int someState;@Before(com.xyz.myapp.SystemArchitecture.businessService())public void recordServiceUsage() {// ...}}

這個(gè)perthis子句的效果是每個(gè)獨立的service對象執行時(shí)都會(huì )創(chuàng )建一個(gè)切面實(shí)例(切入點(diǎn)表達式所匹配的連接點(diǎn)上的每一個(gè)獨立的對象都會(huì )綁定到‘this‘上)。 service對象的每個(gè)方法在第一次執行的時(shí)候創(chuàng )建切面實(shí)例。切面在service對象失效的同時(shí)失效。 在切面實(shí)例被創(chuàng )建前,所有的通知都不會(huì )被執行,一旦切面對象創(chuàng )建完成,定義的通知將會(huì )在匹配的連接點(diǎn)上執行,但是只有當service對象是和切面關(guān)聯(lián)的才可以。 如果想要知道更多關(guān)于per-clauses的信息,請參閱 AspectJ 編程指南。

‘pertarget‘實(shí)例模型的跟“perthis”完全一樣,只不過(guò)是為每個(gè)匹配于連接點(diǎn)的獨立目標對象創(chuàng )建一個(gè)切面實(shí)例。

6.2.7. 例子

現在你已經(jīng)看到了每個(gè)獨立的部分是如何運作的了,是時(shí)候把他們放到一起做一些有用的事情了!

因為并發(fā)的問(wèn)題,有時(shí)候business services可能會(huì )失?。ɡ?,死鎖失?。?。如果重新嘗試一下,很有可能就會(huì )成功。 對于business services來(lái)說(shuō),重試幾次是很正常的(Idempotent操作不需要用戶(hù)參與,否則會(huì )得出矛盾的結論) 我們可能需要透明的重試操作以避免讓客戶(hù)看見(jiàn) PessimisticLockingFailureException 例外被拋出。 很明顯,在一個(gè)橫切多層的情況下,這是非常有必要的,因此通過(guò)切面來(lái)實(shí)現是很理想的。

因為我們想要重試操作,我們會(huì )需要使用到環(huán)繞通知,這樣我們就可以多次調用proceed()方法。下面是簡(jiǎn)單的切面實(shí)現:

@Aspectpublic class ConcurrentOperationExecutor implements Ordered {private static final int DEFAULT_MAX_RETRIES = 2;private int maxRetries = DEFAULT_MAX_RETRIES;private int order = 1;public void setMaxRetries(int maxRetries) {this.maxRetries = maxRetries;}public int getOrder() {return this.order;}public void setOrder(int order) {this.order = order;}@Around("com.xyz.myapp.SystemArchitecture.businessService()")public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {int numAttempts = 0;PessimisticLockingFailureException lockFailureException;do {numAttempts++;try {return pjp.proceed();}catch(PessimisticLockingFailureException ex) {lockFailureException = ex;}}while(numAttempts <= this.maxRetries);throw lockFailureException;}}

請注意切面實(shí)現了 Ordered 接口,這樣我們就可以把切面的優(yōu)先級設定為高于事務(wù)通知(我們每次重試的時(shí)候都想要在一個(gè)全新的事務(wù)中進(jìn)行)。 maxRetriesorder 屬性都可以在Spring中配置。 主要的動(dòng)作在 doConcurrentOperation 這個(gè)環(huán)繞通知中發(fā)生。 請注意這個(gè)時(shí)候我們所有的 businessService() 方法都會(huì )使用這個(gè)重試策略。 我們首先會(huì )嘗試處理,然后如果我們得到一個(gè) PessimisticLockingFailureException 意外,我們只需要簡(jiǎn)單的重試,直到我們耗盡所有預設的重試次數。

對應的Spring配置如下:

<aop:aspectj-autoproxy/><bean id="concurrentOperationExecutor"class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor"><property name="maxRetries" value="3"/><property name="order" value="100"/></bean>

為了改進(jìn)切面,使之僅僅重試idempotent操作,我們可以定義一個(gè) Idempotent 注解:

@Retention(RetentionPolicy.RUNTIME)public @interface Idempotent {// marker annotation}

并且對service操作的實(shí)現進(jìn)行注解。 這樣如果你只希望改變切面使得idempotent的操作會(huì )嘗試多次,你只需要改寫(xiě)切入點(diǎn)表達式,這樣只有 @Idempotent 操作會(huì )匹配:

@Around("com.xyz.myapp.SystemArchitecture.businessService() && " +"@annotation(com.xyz.myapp.service.Idempotent)")public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {...}

6.3. Schema-based AOP support

如果你無(wú)法使用Java 5,或者你比較喜歡使用XML格式,Spring2.0也提供了使用新的"aop"命名空間來(lái)定義一個(gè)切面。 和使用@AspectJ風(fēng)格完全一樣,切入點(diǎn)表達式和通知類(lèi)型同樣得到了支持,因此在這一節中我們將著(zhù)重介紹新的 語(yǔ)法 和回顧前面我們所討論的如何寫(xiě)一個(gè)切入點(diǎn)表達式和通知參數的綁定(Section 6.2, “@AspectJ支持”)。

使用本章所介紹的aop命名空間標簽(aop namespace tag),你需要引入Appendix A, XML Schema-based configuration中提及的spring-aop schema。 參見(jiàn)Section A.2.6, “The aop schema”。

在Spring的配置文件中,所有的切面和通知器都必須定義在 <aop:config> 元素內部。 一個(gè)application context可以包含多個(gè) <aop:config>。 一個(gè) <aop:config> 可以包含pointcut,advisor和aspect元素(注意它們必須按照這樣的順序進(jìn)行聲明)。

Warning

<aop:config>風(fēng)格的配置使得對Spring auto-proxying 機制的使用變得很笨重。如果你已經(jīng)通過(guò)BeanNameAutoProxyCreator或類(lèi)似的東西使用顯式的auto-proxying將會(huì )引發(fā)問(wèn)題 (例如通知沒(méi)有被織入)。推薦的使用模式是只使用<aop:config>風(fēng)格或只使用 AutoProxyCreator風(fēng)格

6.3.1. 聲明一個(gè)切面

有了schema的支持,切面就和常規的Java對象一樣被定義成application context中的一個(gè)bean。 對象的字段和方法提供了狀態(tài)和行為信息,XML文件則提供了切入點(diǎn)和通知信息。

切面使用<aop:aspect>來(lái)聲明,backing bean(支持bean)通過(guò) ref 屬性來(lái)引用:

<aop:config><aop:aspect id="myAspect" ref="aBean">...</aop:aspect></aop:config><bean id="aBean" class="...">...</bean>

切面的支持bean(上例中的"aBean")可以象其他Spring bean一樣被容器管理配置以及依賴(lài)注入。

6.3.2. 聲明一個(gè)切入點(diǎn)

切入點(diǎn)可以在切面里面聲明,這種情況下切入點(diǎn)只在切面內部可見(jiàn)。切入點(diǎn)也可以直接在<aop:config>下定義,這樣就可以使多個(gè)切面和通知器共享該切入點(diǎn)。

一個(gè)描述service層中表示所有service執行的切入點(diǎn)可以如下定義:

<aop:config><aop:pointcut id="businessService"expression="execution(* com.xyz.myapp.service.*.*(..))"/></aop:config>

注意切入點(diǎn)表達式本身使用了 Section 6.2, “@AspectJ支持” 中描述的AspectJ 切入點(diǎn)表達式語(yǔ)言。 如果你在Java 5環(huán)境下使用基于schema的聲明風(fēng)格,可參考切入點(diǎn)表達式類(lèi)型中定義的命名式切入點(diǎn),不過(guò)這在JDK1.4及以下版本中是不被支持的(因為依賴(lài)于Java 5中的AspectJ反射API)。 所以在JDK 1.5中,上面的切入點(diǎn)的另外一種定義形式如下:

<aop:config><aop:pointcut id="businessService"expression="com.xyz.myapp.SystemArchitecture.businessService()"/></aop:config>

假定你有 Section 6.2.3.3, “共享常見(jiàn)的切入點(diǎn)(pointcut)定義”中說(shuō)描述的 SystemArchitecture 切面。

在切面里面聲明一個(gè)切入點(diǎn)和聲明一個(gè)頂級的切入點(diǎn)非常類(lèi)似:

<aop:config><aop:aspect id="myAspect" ref="aBean"><aop:pointcut id="businessService"expression="execution(* com.xyz.myapp.service.*.*(..))"/>...</aop:aspect></aop:config>

當需要連接子表達式的時(shí)候,‘&‘在XML中用起來(lái)非常不方便,所以關(guān)鍵字‘a(chǎn)nd‘, ‘or‘ 和 ‘not‘可以分別用來(lái)代替‘&‘, ‘||‘ 和 ‘!‘。

注意這種方式定義的切入點(diǎn)通過(guò)XML id來(lái)查找,并且不能定義切入點(diǎn)參數。在基于schema的定義風(fēng)格中命名切入點(diǎn)支持較之@AspectJ風(fēng)格受到了很多的限制。

6.3.3. 聲明通知

和@AspectJ風(fēng)格一樣,基于schema的風(fēng)格也支持5種通知類(lèi)型并且兩者具有同樣的語(yǔ)義。

6.3.3.1. 通知(Advice)

Before通知在匹配方法執行前進(jìn)入。在<aop:aspect>里面使用<aop:before>元素進(jìn)行聲明。

<aop:aspect id="beforeExample" ref="aBean"><aop:beforepointcut-ref="dataAccessOperation"method="doAccessCheck"/>...</aop:aspect>

這里 dataAccessOperation 是一個(gè)頂級(<aop:config>)切入點(diǎn)的id。 要定義內置切入點(diǎn),可將 pointcut-ref 屬性替換為 pointcut 屬性:

<aop:aspect id="beforeExample" ref="aBean"><aop:beforepointcut="execution(* com.xyz.myapp.dao.*.*(..))"method="doAccessCheck"/>...</aop:aspect>

我們已經(jīng)在@AspectJ風(fēng)格章節中討論過(guò)了,使用命名切入點(diǎn)能夠明顯的提高代碼的可讀性。

Method屬性標識了提供了通知的主體的方法(doAccessCheck)。這個(gè)方法必須定義在包含通知的切面元素所引用的bean中。 在一個(gè)數據訪(fǎng)問(wèn)操作執行之前(執行連接點(diǎn)和切入點(diǎn)表達式匹配),切面中的"doAccessCheck"會(huì )被調用。

6.3.3.2. 返回后通知(After returning advice)

After returning通知在匹配的方法完全執行后運行。和Before通知一樣,可以在<aop:aspect>里面聲明。例如:

<aop:aspect id="afterReturningExample" ref="aBean"><aop:after-returningpointcut-ref="dataAccessOperation"method="doAccessCheck"/>...</aop:aspect>

和@AspectJ風(fēng)格一樣,通知主體可以接收返回值。使用returning屬性來(lái)指定接收返回值的參數名:

<aop:aspect id="afterReturningExample" ref="aBean"><aop:after-returningpointcut-ref="dataAccessOperation"returning="retVal"method="doAccessCheck"/>...</aop:aspect>

doAccessCheck方法必須聲明一個(gè)名字叫 retVal 的參數。 參數的類(lèi)型強制匹配,和先前我們在@AfterReturning中講到的一樣。例如,方法簽名可以這樣聲明:

public void doAccessCheck(Object retVal) {...

6.3.3.3. 拋出異常后通知(After throwing advice)

After throwing通知在匹配方法拋出異常退出時(shí)執行。在 <aop:aspect> 中使用after-throwing元素來(lái)聲明:

<aop:aspect id="afterThrowingExample" ref="aBean"><aop:after-throwingpointcut-ref="dataAccessOperation"method="doRecoveryActions"/>...</aop:aspect>

和@AspectJ風(fēng)格一樣,可以從通知體中獲取拋出的異常。 使用throwing屬性來(lái)指定異常的名稱(chēng),用這個(gè)名稱(chēng)來(lái)獲取異常:

<aop:aspect id="afterThrowingExample" ref="aBean"><aop:after-throwingpointcut-ref="dataAccessOperation"thowing="dataAccessEx"method="doRecoveryActions"/>...</aop:aspect>

doRecoveryActions方法必須聲明一個(gè)名字為 dataAccessEx 的參數。 參數的類(lèi)型強制匹配,和先前我們在@AfterThrowing中講到的一樣。例如:方法簽名可以如下這般聲明:

public void doRecoveryActions(DataAccessException dataAccessEx) {...

6.3.3.4. 后通知(After (finally) advice)

After (finally)通知在匹配方法退出后執行。使用 after 元素來(lái)聲明:

<aop:aspect id="afterFinallyExample" ref="aBean"><aop:afterpointcut-ref="dataAccessOperation"method="doReleaseLock"/>...</aop:aspect>

6.3.3.5. 通知

Around通知是最后一種通知類(lèi)型。Around通知在匹配方法運行期的“周?chē)眻绦小?它有機會(huì )在目標方法的前面和后面執行,并決定什么時(shí)候運行,怎么運行,甚至是否運行。 Around通知經(jīng)常在需要在一個(gè)方法執行前或后共享狀態(tài)信息,并且是線(xiàn)程安全的情況下使用(啟動(dòng)和停止一個(gè)計時(shí)器就是一個(gè)例子)。 注意選擇能滿(mǎn)足你需求的最簡(jiǎn)單的通知類(lèi)型(i.e.如果簡(jiǎn)單的before通知就能做的事情絕對不要使用around通知)。

Around通知使用 aop:around 元素來(lái)聲明。 通知方法的第一個(gè)參數的類(lèi)型必須是 ProceedingJoinPoint 類(lèi)型。 在通知的主體中,調用 ProceedingJoinPointproceed() 方法來(lái)執行真正的方法。 proceed 方法也可能會(huì )被調用并且傳入一個(gè) Object[] 對象 - 該數組將作為方法執行時(shí)候的參數。 參見(jiàn) Section 6.2.4.5, “環(huán)繞通知(Around Advice)” 中提到的一些注意點(diǎn)。

<aop:aspect id="aroundExample" ref="aBean"><aop:aroundpointcut-ref="businessService"method="doBasicProfiling"/>...</aop:aspect>

doBasicProfiling 通知的實(shí)現和@AspectJ中的例子完全一樣(當然要去掉注解):

public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {// start stopwatchObject retVal = pjp.proceed();// stop stopwatchreturn retVal;}

6.3.3.6. 通知參數

Schema-based聲明風(fēng)格和@AspectJ支持一樣,支持通知的全名形式 - 通過(guò)通知方法參數名字來(lái)匹配切入點(diǎn)參數。 參見(jiàn) Section 6.2.4.6, “通知參數(Advice parameters)” 獲取詳細信息。

如果你希望顯式指定通知方法的參數名(而不是依靠先前提及的偵測策略),可以通過(guò) arg-names 屬性來(lái)實(shí)現。示例如下:

<aop:beforepointcut="com.xyz.lib.Pointcuts.anyPublicMethod() and @annotation(auditable)"method="audit"arg-names="auditable"/>

The arg-names attribute accepts a comma-delimited list of parameter names.

arg-names屬性接受由逗號分割的參數名列表。

請看下面這個(gè)基于XSD風(fēng)格的更復雜一些的實(shí)例,它展示了關(guān)聯(lián)多個(gè)強類(lèi)型參數的環(huán)繞通知的使用。

首先,服務(wù)接口及它的實(shí)現將被通知:

package x.y.service;public interface FooService {Foo getFoo(String fooName, int age);}// the attendant implementation (defined in another file of course)public class DefaultFooService implements FooService {public Foo getFoo(String name, int age) {return new Foo(name, age);}}

下一步(無(wú)可否認的)是切面。注意實(shí)際上profile(..)方法 接受多個(gè)強類(lèi)型(strongly-typed)參數,第一個(gè)參數是方法調用時(shí)要執行的連接點(diǎn),該參數指明了 profile(..)方法被用作一個(gè)環(huán)繞通知:

package x.y;import org.aspectj.lang.ProceedingJoinPoint;import org.springframework.util.StopWatch;public class SimpleProfiler {public Object profile(ProceedingJoinPoint call, String name, int age) throws Throwable {StopWatch clock = new StopWatch("Profiling for ‘" + name + "‘ and ‘" + age + "‘");try {clock.start(call.toShortString());return call.proceed();} finally {clock.stop();System.out.println(clock.prettyPrint());}}}

最后,下面是為一個(gè)特定的連接點(diǎn)執行上面的通知所必需的XML配置:

<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"><!-- this is the object that will be proxied by Spring‘s AOP infrastructure --><bean id="fooService" class="x.y.service.DefaultFooService"/><!-- this is the actual advice itself --><bean id="profiler" class="x.y.SimpleProfiler"/><aop:config><aop:aspect ref="profiler"><aop:pointcut id="theExecutionOfSomeFooServiceMethod"expression="execution(* x.y.service.FooService.getFoo(String,int))and args(name, age)"/><aop:around pointcut-ref="theExecutionOfSomeFooServiceMethod"method="profile"/></aop:aspect></aop:config></beans>

如果使用下面的驅動(dòng)腳本,我們將在標準輸出上得到如下的輸出:

import org.springframework.beans.factory.BeanFactory;import org.springframework.context.support.ClassPathXmlApplicationContext;import x.y.service.FooService;public final class Boot {public static void main(final String[] args) throws Exception {BeanFactory ctx = new ClassPathXmlApplicationContext("x/y/plain.xml");FooService foo = (FooService) ctx.getBean("fooService");foo.getFoo("Pengo", 12);}}
StopWatch ‘Profiling for ‘Pengo‘ and ‘12‘‘: running time (millis) = 0-----------------------------------------ms     %     Task name-----------------------------------------00000  ?  execution(getFoo)

6.3.3.7. 通知順序

當同一個(gè)切入點(diǎn)(執行方法)上有多個(gè)通知需要執行時(shí),執行順序規則在 Section 6.2.4.7, “通知(Advice)順序” 已經(jīng)提及了。 切面的優(yōu)先級通過(guò)切面的支持bean是否實(shí)現了Ordered接口來(lái)決定。

6.3.4. 引入

Intrduction (在A(yíng)spectJ中成為inter-type聲明)允許一個(gè)切面聲明一個(gè)通知對象實(shí)現指定接口,并且提供了一個(gè)接口實(shí)現類(lèi)來(lái)代表這些對象。

aop:aspect 內部使用 aop:declare-parents 元素定義Introduction。 該元素用于用來(lái)聲明所匹配的類(lèi)型有了一個(gè)新的父類(lèi)型(所以有了這個(gè)名字)。 例如,給定接口 UsageTracked,以及這個(gè)接口的一個(gè)實(shí)現類(lèi) DefaultUsageTracked, 下面聲明的切面所有實(shí)現service接口的類(lèi)同時(shí)實(shí)現 UsageTracked 接口。(比如為了通過(guò)JMX暴露statistics。)

<aop:aspect id="usageTrackerAspect" ref="usageTracking"><aop:declare-parentstypes-matching="com.xzy.myapp.service.*+",implement-interface="UsageTracked"default-impl="com.xyz.myapp.service.tracking.DefaultUsageTracked"/><aop:beforepointcut="com.xyz.myapp.SystemArchitecture.businessService()and this(usageTracked)"method="recordUsage"/></aop:aspect>

usageTracking bean的支持類(lèi)可以包含下面的方法:

public void recordUsage(UsageTracked usageTracked) {usageTracked.incrementUseCount();}

欲實(shí)現的接口由 implement-interface 屬性來(lái)指定。 types-matching 屬性的值是一個(gè)AspectJ類(lèi)型模式:- 任何匹配類(lèi)型的bean會(huì )實(shí)現 UsageTracked 接口。 注意在Before通知的例子中,srevice bean可以用作 UsageTracked 接口的實(shí)現。 如果編程形式訪(fǎng)問(wèn)一個(gè)bean,你可以這樣來(lái)寫(xiě):

UsageTracked usageTracked = (UsageTracked) context.getBean("myService");

6.3.5. 切面實(shí)例化模型

Schema-defined切面僅支持一種實(shí)例化模型就是singlton模型。其他的實(shí)例化模型或許在未來(lái)版本中將得到支持。

6.3.6. Advisors

"advisors"這個(gè)概念來(lái)自Spring1.2對AOP的支持,在A(yíng)spectJ中是沒(méi)有等價(jià)的概念。 advisor就像一個(gè)小的自包含的切面,這個(gè)切面只有一個(gè)通知。 切面自身通過(guò)一個(gè)bean表示,并且必須實(shí)現一個(gè)通知接口, 在 Section 7.3.2, “Spring里的通知類(lèi)型” 中我們會(huì )討論相應的接口。Advisors可以很好的利用AspectJ切入點(diǎn)表達式。

Spring 2.0 通過(guò) <aop:advisor> 元素來(lái)支持advisor 概念。 你將會(huì )發(fā)現它大多數情況下會(huì )和transactional advice一起使用,transactional advice在Spring 2.0中有自己的命名空間。格式如下:

<aop:config><aop:pointcut id="businessService"expression="execution(* com.xyz.myapp.service.*.*(..))"/><aop:advisorpointcut-ref="businessService"advice-ref="tx-advice"/></aop:config><tx:advice id="tx-advice"><tx:attributes><tx:method name="*" propagation="REQUIRED"/></tx:attributes></tx:advice>

和在上面使用的 pointcut-ref 屬性一樣,你還可以使用 pointcut 屬性來(lái)定義一個(gè)內聯(lián)的切入點(diǎn)表達式。

為了定義一個(gè)advisord的優(yōu)先級以便讓通知可以有序,使用 order 屬性來(lái)定義 advisor的值 Ordered 。

6.3.7. 例子

讓我們來(lái)看看在 Section 6.2.7, “例子” 提過(guò)并發(fā)鎖失敗重試的例子,如果使用schema對這個(gè)例子進(jìn)行重寫(xiě)是什么效果。

因為并發(fā)鎖的關(guān)系,有時(shí)候business services可能會(huì )失?。ɡ?,死鎖失?。?。 如果重新嘗試一下,很有可能就會(huì )成功。對于business services來(lái)說(shuō),重試幾次是很正常的(Idempotent操作不需要用戶(hù)參與,否則會(huì )得出矛盾的結論) 我們可能需要透明的重試操作以避免讓客戶(hù)看見(jiàn) PessimisticLockingFailureException 例外被拋出。 很明顯,在一個(gè)橫切多層的情況下,這是非常有必要的,因此通過(guò)切面來(lái)實(shí)現是很理想的。

因為我們想要重試操作,我們會(huì )需要使用到環(huán)繞通知,這樣我們就可以多次調用proceed()方法。 下面是簡(jiǎn)單的切面實(shí)現(只是一個(gè)schema支持的普通Java 類(lèi)):

public class ConcurrentOperationExecutor implements Ordered {private static final int DEFAULT_MAX_RETRIES = 2;private int maxRetries = DEFAULT_MAX_RETRIES;private int order = 1;public void setMaxRetries(int maxRetries) {this.maxRetries = maxRetries;}public int getOrder() {return this.order;}public void setOrder(int order) {this.order = order;}public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {int numAttempts = 0;PessimisticLockingFailureException lockFailureException;do {numAttempts++;try {return pjp.proceed();}catch(PessimisticLockingFailureException ex) {lockFailureException = ex;}}while(numAttempts <= this.maxRetries);throw lockFailureException;}}

請注意切面實(shí)現了 Ordered 接口,這樣我們就可以把切面的優(yōu)先級設定為高于事務(wù)通知(我們每次重試的時(shí)候都想要在一個(gè)全新的事務(wù)中進(jìn)行)。 maxRetriesorder 屬性都可以在Spring中配置。 主要的動(dòng)作在 doConcurrentOperation 這個(gè)環(huán)繞通知中發(fā)生。 請注意這個(gè)時(shí)候我們所有的 businessService() 方法都會(huì )使用這個(gè)重試策略。 我們首先會(huì )嘗試處理,然后如果我們得到一個(gè) PessimisticLockingFailureException 異常,我們只需要簡(jiǎn)單的重試,直到我們耗盡所有預設的重試次數。

這個(gè)類(lèi)跟我們在@AspectJ的例子中使用的是相同的,只是沒(méi)有使用注解。

對應的Spring配置如下:

<aop:config><aop:aspect id="concurrentOperationRetry" ref="concurrentOperationExecutor"><aop:pointcut id="idempotentOperation"expression="execution(* com.xyz.myapp.service.*.*(..))"/><aop:aroundpointcut-ref="idempotentOperation"method="doConcurrentOperation"/></aop:aspect></aop:config><bean id="concurrentOperationExecutor"class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor"><property name="maxRetries" value="3"/><property name="order" value="100"/></bean>

請注意我們現在假設所有的bussiness services都是idempotent。如果不是這樣,我們可以改寫(xiě)切面,加上 Idempotent 注解,讓它只調用idempotent:

@Retention(RetentionPolicy.RUNTIME)public @interface Idempotent {// marker annotation}

并且對service操作的實(shí)現進(jìn)行注解。這樣如果你只希望改變切面使得idempotent的操作會(huì )嘗試多次,你只需要改寫(xiě)切入點(diǎn)表達式,這樣只有 @Idempotent 操作會(huì )匹配:

  <aop:pointcut id="idempotentOperation"expression="execution(* com.xyz.myapp.service.*.*(..)) and@annotation(com.xyz.myapp.service.Idempotent)"/>

6.4. AOP聲明風(fēng)格的選擇

當你確定切面是實(shí)現一個(gè)給定需求的最佳方法時(shí),你如何選擇是使用Spring AOP還是AspectJ,以及選擇 Aspect語(yǔ)言(代碼)風(fēng)格、@AspectJ聲明風(fēng)格或XML風(fēng)格?這個(gè)決定會(huì )受到多個(gè)因素的影響,包括應用的需求、 開(kāi)發(fā)工具和小組對AOP的精通程度。

6.4.1. Spring AOP還是完全用AspectJ?

做能起作用的最簡(jiǎn)單的事。Spring AOP比完全使用AspectJ更加簡(jiǎn)單,因為它不需要引入AspectJ的編譯器/織入器到你開(kāi)發(fā)和構建過(guò)程中。 如果你僅僅需要在Spring bean上通知執行操作,那么Spring AOP是合適的選擇。如果你需要通知domain對象或其它沒(méi)有在Spring容器中 管理的任意對象,那么你需要使用AspectJ。如果你想通知除了簡(jiǎn)單的方法執行之外的連接點(diǎn)(如:調用連接點(diǎn)、字段get或set的連接點(diǎn)等等), 也需要使用AspectJ。

當使用AspectJ時(shí),你可以選擇使用AspectJ語(yǔ)言(也稱(chēng)為“代碼風(fēng)格”)或@AspectJ注解風(fēng)格。 如果切面在你的設計中扮演一個(gè)很大的角色,并且你能在Eclipse中使用AspectJ Development Tools (AJDT), 那么首選AspectJ語(yǔ)言 :- 因為該語(yǔ)言專(zhuān)門(mén)被設計用來(lái)編寫(xiě)切面,所以會(huì )更清晰、更簡(jiǎn)單。如果你沒(méi)有使用 Eclipse,或者在你的應用中只有很少的切面并沒(méi)有作為一個(gè)主要的角色,你或許應該考慮使用@AspectJ風(fēng)格 并在你的IDE中附加一個(gè)普通的Java編輯器,并且在你的構建腳本中增加切面織入(鏈接)的段落。

6.4.2. Spring AOP中使用@AspectJ還是XML?

如果你選擇使用Spring AOP,那么你可以選擇@AspectJ或者XML風(fēng)格??偟膩?lái)說(shuō),如果你使用Java 5, 我們建議使用@AspectJ風(fēng)格。顯然如果你不是運行在Java 5上,XML風(fēng)格是最佳選擇。XML和@AspectJ 之間權衡的細節將在下面進(jìn)行討論。

XML風(fēng)格對現有的Spring用戶(hù)來(lái)說(shuō)更加習慣。它可以使用在任何Java級別中(參考連接點(diǎn)表達式內部的命名連接點(diǎn),雖然它也需要Java 5) 并且通過(guò)純粹的POJO來(lái)支持。當使用AOP作為工具來(lái)配置企業(yè)服務(wù)時(shí)(一個(gè)好的例子是當你認為連接點(diǎn)表達式是你的配置中的一部分時(shí), 你可能想單獨更改它)XML會(huì )是一個(gè)很好的選擇。對于XML風(fēng)格,從你的配置中可以清晰的表明在系統中存在那些切面。

XML風(fēng)格有兩個(gè)缺點(diǎn)。第一是它不能完全將需求實(shí)現的地方封裝到一個(gè)位置。DRY原則中說(shuō)系統中的每一項知識都必須具有單一、無(wú)歧義、權威的表示。 當使用XML風(fēng)格時(shí),如何實(shí)現一個(gè)需求的知識被分割到支撐類(lèi)的聲明中以及XML配置文件中。當使用@AspectJ風(fēng)格時(shí)就只有一個(gè)單獨的模塊 -切面- 信息被封裝了起來(lái)。 第二是XML風(fēng)格同@AspectJ風(fēng)格所能表達的內容相比有更多的限制:僅僅支持"singleton"切面實(shí)例模型,并且不能在XML中組合命名連接點(diǎn)的聲明。 例如,在@AspectJ風(fēng)格中我們可以編寫(xiě)如下的內容:

  @Pointcut(execution(* get*()))public void propertyAccess() {}@Pointcut(execution(org.xyz.Account+ *(..))public void operationReturningAnAccount() {}@Pointcut(propertyAccess() && operationReturningAnAccount())public void accountPropertyAccess() {}

在XML風(fēng)格中能聲明開(kāi)頭的兩個(gè)連接點(diǎn):

  <aop:pointcut id="propertyAccess"expression="execution(* get*())"/><aop:pointcut id="operationReturningAnAccount"expression="execution(org.xyz.Account+ *(..))"/>

但是不能通過(guò)組合這些來(lái)定義accountPropertyAccess連接點(diǎn)

@AspectJ風(fēng)格支持其它的實(shí)例模型以及更豐富的連接點(diǎn)組合。它具有將將切面保持為一個(gè)模塊單元的優(yōu)點(diǎn)。 還有一個(gè)優(yōu)點(diǎn)就是@AspectJ切面能被Spring AOP和AspectJ兩者都理解 - 所以如果稍后你認為你需要AspectJ 的能力去實(shí)現附加的需求,那么你非常容易轉移到基于A(yíng)spectJ的途徑??偠灾?,我們更喜歡@AspectJ風(fēng)格只要你有切面 去做超出簡(jiǎn)單的“配置”企業(yè)服務(wù)之外的事情。

6.5. 混合切面類(lèi)型

我們完全可以混合使用以下幾種風(fēng)格的切面定義:使用自動(dòng)代理的@AspectJ 風(fēng)格的切面,schema-defined <aop:aspect> 的切面,和用 <aop:advisor> 聲明的advisor,甚至是使用Spring 1.2風(fēng)格的代理和攔截器。 由于以上幾種風(fēng)格的切面定義的都使用了相同的底層機制,因此可以很好的共存。

6.6. 代理機制

Spring AOP部分使用JDK動(dòng)態(tài)代理或者CGLIB來(lái)為目標對象創(chuàng )建代理。(建議盡量使用JDK的動(dòng)態(tài)代理)

如果被代理的目標對象實(shí)現了至少一個(gè)接口,則會(huì )使用JDK動(dòng)態(tài)代理。所有該目標類(lèi)型實(shí)現的接口都將被代理。若該目標對象沒(méi)有實(shí)現任何接口,則創(chuàng )建一個(gè)CGLIB代理。

如果你希望強制使用CGLIB代理,(例如:希望代理目標對象的所有方法,而不只是實(shí)現自接口的方法)那也可以。但是需要考慮以下問(wèn)題:

  • 無(wú)法通知(advise)Final 方法,因為他們不能被覆寫(xiě)。

  • 你需要將CGLIB 2二進(jìn)制發(fā)行包放在classpath下面,與之相較JDK本身就提供了動(dòng)態(tài)代理

強制使用CGLIB代理需要將 <aop:config>proxy-target-class 屬性設為true:

<aop:config proxy-target-class="true">...</aop:config>

當需要使用CGLIB代理和@AspectJ自動(dòng)代理支持,請按照如下的方式設置 <aop:aspectj-autoproxy>proxy-target-class 屬性:

<aop:aspectj-autoproxy proxy-target-class="true"/>

6.7. 編程方式創(chuàng )建@AspectJ代理

除了在配置文件中使用 <aop:config> 或者 <aop:aspectj-autoproxy> 來(lái)聲明切面。 同樣可以通過(guò)編程方式來(lái)創(chuàng )建代理通知(advise)目標對象。關(guān)于Spring AOP API的詳細介紹,請參看下一章。這里我們重點(diǎn)介紹自動(dòng)創(chuàng )建代理。

類(lèi) org.springframework.aop.aspectj.annotation.AspectJProxyFactory 可以為@AspectJ切面的目標對象創(chuàng )建一個(gè)代理。該類(lèi)的基本用法非常簡(jiǎn)單,示例如下。請參看Javadoc獲取更詳細的信息。

// create a factory that can generate a proxy for the given target objectAspectJProxyFactory factory = new AspectJProxyFactory(targetObject);// add an aspect, the class must be an @AspectJ aspect// you can call this as many times as you need with different aspectsfactory.addAspect(SecurityManager.class);// you can also add existing aspect instances, the type of the object supplied must be an @AspectJ aspectfactory.addAspect(usageTracker);// now get the proxy object...MyInterfaceType proxy = factory.getProxy();

6.8. 在Spring應用中使用AspectJ

到目前為止本章討論的一直是純Spring AOP。 在這一節里面我們將介紹如何使用AspectJ compiler/weaver來(lái)代替Spring AOP或者作為它的補充,因為有些時(shí)候Spring AOP單獨提供的功能也許并不能滿(mǎn)足你的需要。

Spring提供了一個(gè)小巧的AspectJ aspect library (你可以在程序發(fā)行版本中單獨使用 spring-aspects.jar 文件,并將其加入到classpath下以使用其中的切面)。 Section 6.8.1, “在Spring中使用AspectJ來(lái)為domain object進(jìn)行依賴(lài)注入”Section 6.8.2, “Spring中其他的AspectJ切面” 討論了該庫和如何使用該庫。 Section 6.8.3, “使用Spring IoC來(lái)配置AspectJ的切面” 討論了如何對通過(guò)AspectJ compiler織入的AspectJ切面進(jìn)行依賴(lài)注入。 最后Section 6.8.4, “在Spring應用中使用AspectJ Load-time weaving(LTW)”介紹了使用AspectJ的Spring應用程序如何裝載期織入(load-time weaving)。

6.8.1. 在Spring中使用AspectJ來(lái)為domain object進(jìn)行依賴(lài)注入

Spring容器對application context中定義的bean進(jìn)行實(shí)例化和配置。 同樣也可以通過(guò)bean factory來(lái)為一個(gè)已經(jīng)存在且已經(jīng)定義為spring bean的對象應用所包含的配置信息。 spring-aspects.jar中包含了一個(gè)annotation-driven的切面,提供了能為任何對象進(jìn)行依賴(lài)注入的能力。 這樣的支持旨在為 脫離容器管理 創(chuàng )建的對象進(jìn)行依賴(lài)注入。 Domain object經(jīng)常處于這樣的情形:它們可能是通過(guò) new 操作符創(chuàng )建的對象, 也可能是ORM工具查詢(xún)數據庫的返回結果對象。

org.springframework.orm.hibernate.support 中的類(lèi) DependencyInjectionInterceptorFactoryBean 可以讓Spring為Hibernate創(chuàng )建并且配置prototype類(lèi)型的domain object(使用自動(dòng)裝配或者確切命名的bean原型定義)。 當然,攔截器不支持配置你編程方式創(chuàng )建的對象而非檢索數據庫返回的對象。 其他framework也會(huì )提供類(lèi)似的技術(shù)。仍是那句話(huà),Be Pragramatic選擇能滿(mǎn)足你需求的方法中最簡(jiǎn)單的那個(gè)。 請注意前面提及的類(lèi) 沒(méi)有 隨Spring發(fā)行包一起發(fā)布。 如果你希望使用該類(lèi),需要從Spring CVS Respository上下載并且自行編譯。 你可以在Spring CVS respository下的 ‘sandbox‘ 目錄下找到該文件。

@Configurable 注解標記了一個(gè)類(lèi)可以通過(guò)Spring-driven方式來(lái)配置。 在最簡(jiǎn)單的情況下,我們只把它當作標記注解:

package com.xyz.myapp.domain;import org.springframework.beans.factory.annotation.Configurable;@Configurablepublic class Account {...}

當只是簡(jiǎn)單地作為一個(gè)標記接口來(lái)使用的時(shí)候,Spring將采用和該已注解的類(lèi)型(比如Account類(lèi))全名 (com.xyz.myapp.domain.Account)一致的bean原型定義來(lái)配置一個(gè)新實(shí)例。 由于一個(gè)bean默認的名字就是它的全名,所以一個(gè)比較方便的辦法就是省略定義中的id屬性:

<bean class="com.xyz.myapp.domain.Account" scope="prototype"><property name="fundsTransferService" ref="fundsTransferService"/>...</bean>

如果你希望明確的指定bean原型定義的名字,你可以在注解中直接定義:

package com.xyz.myapp.domain;import org.springframework.beans.factory.annotation.Configurable;@Configurable("account")public class Account {...}

Spring會(huì )查找名字為"account"的bean定義,并使用它作為原型定義來(lái)配置一個(gè)新的Account對象。

你也可以使用自動(dòng)裝配來(lái)避免手工指定原型定義的名字。 只要設置 @Configurable 注解中的autowire屬性就可以讓Spring來(lái)自動(dòng)裝配了: @Configurable(autowire=Autowire.BY_TYPE) 或者 @Configurable(autowire=Autowire.BY_NAME,這樣就可以按類(lèi)型或者按名字自動(dòng)裝配了。

最后,你可以設置 dependencyCheck 屬性,通過(guò)設置,Spring對新創(chuàng )建和配置的對象的對象引用進(jìn)行校驗 (例如:@Configurable(autowire=Autowire.BY_NAME,dependencyCheck=true) )。 如果這個(gè)屬性被設為true,Spring會(huì )在配置結束后校驗除了primitives和collections類(lèi)型的所有的屬性是否都被賦值了。

僅僅使用注解并沒(méi)有做任何事情。但當注解存在時(shí),spring-aspects.jar中的 AnnotationBeanConfigurerAspect 就起作用了。 實(shí)質(zhì)上切面做了這些:當初始化一個(gè)有 @Configurable 注解的新對象時(shí),Spring按照注解中的屬性來(lái)配置這個(gè)新創(chuàng )建的對象。 要實(shí)現上述的操作,已注解的類(lèi)型必須由AspectJ weaver來(lái)織入 - 你可以使用一個(gè) build-time ant/maven任務(wù)來(lái)完成 (參見(jiàn)AspectJ Development Environment Guide) 或者使用load-time weaving(參見(jiàn) Section 6.8.4, “在Spring應用中使用AspectJ Load-time weaving(LTW)”)。

類(lèi) AnnotationBeanConfigurerAspect 本身也需要Spring來(lái)配置(獲得bean factory的引用,使用bean factory配置新的對象)。 為此Spring AOP命名空間定義了一個(gè)非常方便的標簽。如下所示,可以很簡(jiǎn)單的在application context配置文件包含這個(gè)標簽中。

<aop:spring-configured/>

如果你使用DTD代替Schema,對應的定義如下:

<beanclass="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect"factory-method="aspectOf"/>

在切面配置完成 之前 創(chuàng )建的@Configurable對象實(shí)例會(huì )導致在log中留下一個(gè)warning,并且任何對于該對象的配置都不會(huì )生效。 舉一個(gè)例子,一個(gè)Spring管理配置的bean在被Spring初始化的時(shí)候創(chuàng )建了一個(gè)domain object。 對于這樣的情況,你需要定義bean屬性中的"depends-on"屬性來(lái)手動(dòng)指定該bean依賴(lài)于configuration切面。

<bean id="myService"class="com.xzy.myapp.service.MyService"depends-on="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect">...</bean>

6.8.1.1.  @Configurable object的單元測試

提供 @Configurable 支持的一個(gè)目的就是使得domain object的單元測試可以獨立進(jìn)行,不需要通過(guò)硬編碼查找各種倚賴(lài)關(guān)系。 如果 @Configurable 類(lèi)型沒(méi)有通過(guò)AspectJ織入, 則在單元測試過(guò)程中注解不會(huì )起到任何作用,測試中你可以簡(jiǎn)單的為對象的mock或者stub屬性賦值,并且和正常情況一樣的去使用該對象。 如果 @Configurable 類(lèi)型通過(guò)AspectJ織入, 我們依然可以脫離容器進(jìn)行單元測試,不過(guò)每次創(chuàng )建一個(gè)新的 @Configurable 對象時(shí)都會(huì )看到一個(gè)warning標示該對象不受Spring管理配置。

6.8.1.2. 多application context情況下的處理

AnnotationBeanConfigurerAspect 通過(guò)一個(gè)AspectJ singleton切面來(lái)實(shí)現對 @Configurable 的支持。 一個(gè)singleton切面的作用域和一個(gè)靜態(tài)變量的作用域是一樣的,例如,對于每一個(gè)classloader有一個(gè)切面來(lái)定義類(lèi)型。 這就意味著(zhù)如果你在一個(gè)classloader層次結構中定義了多個(gè)application context的時(shí)候就需要考慮在哪里定義 <aop:spring-configured/> bean和在哪個(gè)classpath下放置Srping-aspects.jar。

考慮一下典型的Spring web項目,一般都是由一個(gè)父application context定義大部分business service和所需要的其他資源,然后每一個(gè)servlet擁有一個(gè)子application context定義。 所有這些context共存于同一個(gè)classloader hierarchy下,因此對于全體context,AnnotationBeanConfigurerAspect 僅可以維護一個(gè)引用。 在這樣的情況下,我們推薦在父application context中定義 <aop:spring-configured/> bean: 這里所定義的service可能是你希望注入domain object的。 這樣做的結果是你不能為子application context中使用@Configurable的domain object配置bean引用(可能你也根本就不希望那么做?。?。

當在一個(gè)容器中部署多個(gè)web-app的時(shí)候,請確保每一個(gè)web-application使用自己的classloader來(lái)加載spring-aspects.jar中的類(lèi)(例如將spring-aspects.jar放在WEB-INF/lib目錄下)。 如果spring-aspects.jar被放在了容器的classpath下(因此也被父classloader加載),則所有的web application將共享一個(gè)aspect實(shí)例,這可能并不是你所想要的。

6.8.2. Spring中其他的AspectJ切面

除了 @Configurable 支持,spring-aspects.jar包含了一個(gè)AspectJ切面可以用來(lái)為那些使用了 @Transactional annotation 的類(lèi)型和方法驅動(dòng)Spring事務(wù)管理(參見(jiàn) Chapter 9, 事務(wù)管理)。 提供這個(gè)的主要目的是有些用戶(hù)希望脫離Spring容器使用Spring的事務(wù)管理。

解析@Transactional annotations的切面是AnnotationTransactionAspect。 當使用這個(gè)切面時(shí),你必須注解這個(gè)實(shí)現類(lèi)(和/或這個(gè)類(lèi)中的方法),而不是這個(gè)類(lèi)實(shí)現的接口(如果有)。 AspectJ允許在接口上注解的Java規則 不被繼承。

類(lèi)之上的一個(gè)@Transactional注解為該類(lèi)中任何public操作的執行指定了默認的事務(wù)語(yǔ)義。

類(lèi)內部方法上的一個(gè)@Transactional注解會(huì )覆蓋類(lèi)注解(如果存在)所給定的默認的事務(wù)語(yǔ)義。 具有public、protected和default修飾符的方法都可以被注解。直接注解protected和default方法是讓這個(gè)操作的執行 獲得事務(wù)劃分的唯一途徑。

對于A(yíng)spectJ程序員,希望使用Spring管理配置和事務(wù)管理支持,不過(guò)他們不想(或者不能)使用注解,spring-aspects.jar也包含了一些抽象切面供你繼承來(lái)提供你自己的切入點(diǎn)定義。 參見(jiàn) AbstractBeanConfigurerAspectAbstractTransactionAspect 的Javadoc獲取更多信息。 作為一個(gè)例子,下面的代碼片斷展示了如何編寫(xiě)一個(gè)切面,然后通過(guò)bean原型定義中和類(lèi)全名匹配的來(lái)配置domian object中所有的實(shí)例:

public aspect DomainObjectConfiguration extends AbstractBeanConfigurerAspect {public DomainObjectConfiguration() {setBeanWiringInfoResolver(new ClassNameBeanWiringInfoResolver());}// the creation of a new bean (any object in the domain model)protected pointcut beanCreation(Object beanInstance) :initialization(new(..)) &&SystemArchitecture.inDomainModel() &&this(beanInstance);}

6.8.3. 使用Spring IoC來(lái)配置AspectJ的切面

當在Spring application中使用AspectJ的時(shí)候,很自然的會(huì )想到用Spring來(lái)管理這些切面。 AspectJ runtime自身負責切面的創(chuàng )建,這意味著(zhù)通過(guò)Spring來(lái)管理AspectJ 創(chuàng )建切面依賴(lài)于切面所使用的AspectJ instantiation model(per-clause)。

大多數AspectJ切面都是 singleton 切面。 管理這些切面非常容易,和通常一樣創(chuàng )建一個(gè)bean定義引用該切面類(lèi)型就可以了,并且在bean定義中包含 ‘factory-method="aspectOf"‘ 這個(gè)屬性。 這確保Spring從AspectJ獲取切面實(shí)例而不是嘗試自己去創(chuàng )建該實(shí)例。示例如下:

<bean id="profiler" class="com.xyz.profiler.Profiler"factory-method="aspectOf"><property name="profilingStrategy" ref="jamonProfilingStrategy"/></bean>

對于non-singleton的切面,最簡(jiǎn)單的配置管理方法是定義一個(gè)bean原型定義并且使用@Configurable支持,這樣就可以在切面被AspectJ runtime創(chuàng )建后管理它們。

如果你希望一些@AspectJ切面使用AspectJ來(lái)織入(例如使用load-time織入domain object) 和另一些@AspectJ切面使用Spring AOP,而這些切面都是由Spring來(lái)管理的,那你就需要告訴Spring AOP @AspectJ自動(dòng)代理支持那些切面需要被自動(dòng)代理。 你可以通過(guò)在 <aop:aspectj-autoproxy> 聲明中使用一個(gè)或多個(gè) <include/>。 每一個(gè)指定了一種命名格式,只有bean命名至少符合其中一種情況下才會(huì )使用Spring AOP自動(dòng)代理配置:

<aop:aspectj-autoproxy><include name="thisBean"/><include name="thatBean"/></aop:aspectj-autoproxy>

6.8.4. 在Spring應用中使用AspectJ Load-time weaving(LTW)

Load-time weaving(LTW)指的是在虛擬機載入字節碼文件時(shí)動(dòng)態(tài)織入AspectJ切面。 關(guān)于LTW的詳細信息,請查看 LTW section of the AspectJ Development Environment Guide。 在這里我們重點(diǎn)來(lái)看一下Java 5環(huán)境下Spring應用如何配置LTW。

LTW需要定義一個(gè) aop.xml,并將其置于META-INF目錄。 AspectJ會(huì )自動(dòng)查找所有可見(jiàn)的classpath下的META-INF/aop.xml文件,并且通過(guò)定義內容的合集來(lái)配置自身。

一個(gè)基本的META-INF/aop.xml文件應該如下所示:

<!DOCTYPE aspectj PUBLIC"-//AspectJ//DTD//EN"	"http://www.eclipse.org/aspectj/dtd/aspectj.dtd"><aspectj><weaver><include within="com.xyz.myapp..*"/></weaver></aspectj>

<include/>‘的內容告訴AspectJ那些類(lèi)型需要被納入織入過(guò)程。使用包名前綴并加上"..*"(表示該子包中的所有類(lèi)型)是一個(gè)不錯的默認設定。 使用include元素是非常重要的,不然AspectJ會(huì )查找每一個(gè)應用里面用到的類(lèi)型(包括Spring的庫和其它許多相關(guān)庫)。通常你并不希望織入這些類(lèi)型并且不愿意承擔AspectJ嘗試去匹配的開(kāi)銷(xiāo)。

希望在日志中記錄LTW的活動(dòng),請添加如下選項:

<!DOCTYPE aspectj PUBLIC"-//AspectJ//DTD//EN"    "http://www.eclipse.org/aspectj/dtd/aspectj.dtd"><aspectj><weaveroptions="-showWeaveInfo-XmessageHandlerClass:org.springframework.aop.aspectj.AspectJWeaverMessageHandler"><include within="com.xyz.myapp..*"/></weaver></aspectj>

最后,如果希望精確的控制使用哪些切面,可以使用 aspects。 默認情況下所有定義的切面都將被織入(spring-aspects.jar包含了META-INF/aop.xml,定義了配置管理和事務(wù)管理切面)。 如果你在使用spring-aspects.jar,但是只希望使用配制管理切面而不需要事務(wù)管理的話(huà),你可以像下面那樣定義:

<!DOCTYPE aspectj PUBLIC"-//AspectJ//DTD//EN"	"http://www.eclipse.org/aspectj/dtd/aspectj.dtd"><aspectj><aspects><include within="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect"/></aspects><weaveroptions="-showWeaveInfo -XmessageHandlerClass:org.springframework.aop.aspectj.AspectJWeaverMessageHandler"><include within="com.xyz.myapp..*"/></weaver></aspectj>

在Java 5平臺下,LTW可以通過(guò)虛擬機的參數來(lái)啟用。

-javaagent:<path-to-ajlibs>/aspectjweaver.jar

6.9. 其它資源

更多關(guān)于A(yíng)spectJ的信息可以查看 AspectJ home page。

Eclipse AspectJ by Adrian Colyer et. al. (Addison-Wesley, 2005)全面介紹并提供了AspectJ語(yǔ)言參考。

AspectJ in Action by Ramnivas Laddad (Manning, 2003)是一本非常出色介紹AOP的書(shū)籍;全書(shū)著(zhù)重介紹了AspectJ,但也對一些通用的AOP場(chǎng)景進(jìn)行了比較深入的研究。

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Spring:(二) -- 春風(fēng)拂面之 核心 AOP
InfoQ: Spring 2.0的新特性和應用實(shí)踐
我用一篇文搞懂了《AOP面向切面編程》
Spring高級程序設計 6 Spring AOP 進(jìn)階
Spring
Spring2.X的 AOP支持
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久