今天一看,Blog爬到了第一,又很久沒(méi)更新了,因此就把這兩天正在寫(xiě)的電子書(shū)的一部分貼上吧。今天想找找Spring 2.0 AOP的例子,誰(shuí)料想網(wǎng)上文章一大抄,而且呢,大部分人是直接copy Spring的文檔,自己連個(gè)demo也不寫(xiě)一種的那個(gè),就見(jiàn)一堆文章說(shuō)的云里霧里熱火朝天的AOP,也沒(méi)見(jiàn)著幾個(gè)能跑的例子。最后總算找到了個(gè)兄弟的小短文,有個(gè)能跑的例子,這才算弄好了1.2和2.0的對(duì)比部分。好了,下面是正文(其實(shí)尚未完工,先湊合看吧):
10.3 開(kāi)發(fā)Spring 1.2 AOP應(yīng)用
本節(jié)將會(huì)給大家展示一個(gè)恐怖的例子,FBI特務(wù)人員已經(jīng)介入了您的生活,您所做的一切都在他們的監(jiān)視之中,包括聊QQ,泡MM,這在現(xiàn)實(shí)生活中是真實(shí)存在的,為了民眾的安全和穩(wěn)定,對(duì)嫌疑犯進(jìn)行必要的監(jiān)控是必要的。
注意:本章雖然介紹了多種AOP實(shí)現(xiàn)方式,然而,在實(shí)際項(xiàng)目中只要使用一種就可以達(dá)到目的了(因?yàn)?/span>Spring的AOP存在多種寫(xiě)法,完全掌握還是挺復(fù)雜),其它方式僅供參考,千萬(wàn)不要像孔乙己一樣,研究“茴”字的N種寫(xiě)法,這樣就脫離了學(xué)習(xí)技術(shù)的初衷了:學(xué)習(xí)是為了解決問(wèn)題,不是為了炫耀自己。另外,如果在項(xiàng)目中濫用AOP的后果就是系統(tǒng)的執(zhí)行效率大大降低,甚至配置不當(dāng)會(huì)導(dǎo)致死循環(huán)。記住一個(gè)真理:系統(tǒng)越復(fù)雜,效率越低,出故障的可能越大。另外一條建議:千萬(wàn)不要用AOP在服務(wù)器上記錄日志,或者在服務(wù)器上打印不必要的調(diào)試信息,那樣對(duì)系統(tǒng)只能有害無(wú)益,日志輸出是單線程操作,切記。做項(xiàng)目,一般來(lái)說(shuō)是功能越少越好。高手更多的時(shí)候只能做出破壞力大,不易維護(hù)的垃圾系統(tǒng)。
10.3.1開(kāi)發(fā)Man對(duì)象
這個(gè)項(xiàng)目非常簡(jiǎn)單,仿照上節(jié)內(nèi)容,創(chuàng)建項(xiàng)目并添加Spring開(kāi)發(fā)功能,不同的是添加library的時(shí)候要把Spring 2.0 AOP Libraries加入進(jìn)來(lái)。因?yàn)?/span>Spring 2.0的類庫(kù)是兼容1.2的,所以這里就用2.0了。項(xiàng)目名為Spring1_2AOP。接下來(lái)我們要?jiǎng)?chuàng)建一個(gè)自由人的對(duì)象,他有聊QQ和泡MM這兩個(gè)方法,還有一個(gè)姓名屬性。好了,先建立這個(gè)類:
/** *具有聊QQ和泡MM兩個(gè)行為的人對(duì)象,還有一個(gè)用戶名屬性。 *@authorBeanSoft */ publicclass Man { private String name; public String getName() { returnname; } publicvoid setName(String name) { this.name = name; }
publicvoid qq() { System.out.println("我在聊QQ"); }
publicvoid mm() { System.out.println("我在泡MM"); } } |
清單10.6 Man類源碼
10.3.2開(kāi)發(fā)前置通知(Before advice)對(duì)象:FBI
首先貼一段Spring文檔中關(guān)于Before advice的介紹:
前置通知(Before advice): 在某連接點(diǎn)(join point)之前執(zhí)行的通知,但這個(gè)通知不能阻止連接點(diǎn)前的執(zhí)行(除非它拋出一個(gè)異常)。
說(shuō)通俗點(diǎn)就是寫(xiě)一個(gè)如何處理監(jiān)視結(jié)果的對(duì)象,可以把監(jiān)視結(jié)果打印出來(lái)以作為必要的時(shí)候的呈堂證物,或者派探員立即跟蹤,但是這個(gè)過(guò)程只能在你進(jìn)行某活動(dòng)前進(jìn)行,否則就失去監(jiān)視的意義了,這個(gè)對(duì)象更像“諸葛亮”。詳細(xì)的了解這個(gè)類需要學(xué)習(xí)JDK里面關(guān)于反射部分的內(nèi)容,下面是這個(gè)類的代碼:
import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; /** *聯(lián)邦調(diào)查局的探員將您的所有行動(dòng)都記錄在案。 *@authorBeanSoft */ publicclass FBI implements MethodBeforeAdvice { publicvoid before(Method method, Object[] args, Object target) throws Throwable { Man man = (Man)target; System.err.println("FBI 發(fā)現(xiàn)" + man.getName() + "正在進(jìn)行 " + method.getName() + " 活動(dòng)。"); } } |
清單10.7 FBI類源碼
10.3.3裝配攔截器和Bean
最后要做的,就是創(chuàng)建一個(gè)平民對(duì)象,注意不是自由人哦,因?yàn)槠矫袷请S時(shí)處于FBI的監(jiān)視之下的。這個(gè)對(duì)象本質(zhì)上是類ProxyFactoryBean的一個(gè)示例,這個(gè)類位于包org.springframework.aop.framework下,自由人只能活在這個(gè)代理工廠類的陰影下了,也就是成了平民了。好了,我們把相應(yīng)的Spring配置文件代碼放給大家:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="man" class="Man"> <property name="name"> <value type="java.lang.String">張三</value> </property> </bean> <bean id="fbi" class="FBI" />
<bean id="civilian" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <ref bean="man" /> </property> <property name="interceptorNames"> <list> <value>fbi</value> </list> </property> </bean> </beans> |
清單10.8 Spring AOP 配置文件源碼 applicationContext.xml
在這個(gè)文件中,定義了兩個(gè)bean:man和fbi,都是普通的類定義。復(fù)雜一些的地方在civilian這個(gè)bean的定義中,它要攔截或者監(jiān)視的目標(biāo)(target)是man,負(fù)責(zé)進(jìn)行處理監(jiān)視結(jié)果的對(duì)象(interceptorNames)是fbi,具體進(jìn)行監(jiān)視工作的對(duì)象,就是這個(gè)ProxyFactoryBean,它相當(dāng)于竊聽(tīng)器之類的東西,但是很顯然竊聽(tīng)結(jié)果是需要人來(lái)處理的,那就是FBI。
簡(jiǎn)單說(shuō): man成為了civilian,它被ProxyFactoryBean監(jiān)控,監(jiān)控結(jié)果交給FBI處理。很顯然,如果沒(méi)有國(guó)家,也就沒(méi)有civilian,更談不上FBI了。所以,在這里您不能再去找man,因?yàn)?/span>man在實(shí)際生活中是不存在的,所以您只能找civilian,這樣您才能感覺(jué)到FBI的存在。下面是相關(guān)的bean關(guān)系圖:
圖 10.9 Bean關(guān)系圖
10.3.4測(cè)試和運(yùn)行
OK,如上節(jié)討論,只有當(dāng)平民的時(shí)候才會(huì)被監(jiān)視,現(xiàn)在我們就可以寫(xiě)一個(gè)測(cè)試類來(lái)感受一下平民生活:
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; publicclass AOPTest { publicstaticvoid main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Man man = (Man) ctx.getBean("civilian"); man.qq(); man.mm(); } } |
清單10.9 Spring AOP 測(cè)試類源碼
運(yùn)行一下,您就可以看到可怕的真相:
log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext). log4j:WARN Please initialize the log4j system properly. FBI 發(fā)現(xiàn)張三正在進(jìn)行 qq 活動(dòng)。 FBI 發(fā)現(xiàn)張三正在進(jìn)行 mm 活動(dòng)。 我在聊QQ 我在泡MM |
是不是很恐怖呢?FBI正在監(jiān)控您的一舉一動(dòng),并把這些東西都記錄在案?,F(xiàn)在你應(yīng)該可以了解AOP的過(guò)程了:調(diào)用man這個(gè)bean的任何一個(gè)方法之前,都會(huì)事先通知(調(diào)用)fbi這個(gè)bean并告知相關(guān)的調(diào)用信息,這些信息包括方法(method),參數(shù)(args)以及目標(biāo)對(duì)象(target,這里就是man這個(gè)對(duì)象)。
如果你把上面的代碼改成ctx.getBean(“man”),那么自由人是不會(huì)被監(jiān)控的,所以這時(shí)候您就不會(huì)看到FBI輸出的恐怖信息了。此時(shí)的輸出如下:
log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext). log4j:WARN Please initialize the log4j system properly. 我在聊QQ 我在泡MM |
10.3.5 AOP簡(jiǎn)介和相關(guān)概念
現(xiàn)在我們已經(jīng)寫(xiě)了一個(gè)很簡(jiǎn)單的Spring AOP 例子,現(xiàn)在就給大家簡(jiǎn)單介紹一下相關(guān)的概念,這些信息來(lái)自于Spring的中文文檔。
面向切面編程(AOP)提供另外一種角度來(lái)思考程序結(jié)構(gòu),通過(guò)這種方式彌補(bǔ)了面向?qū)ο缶幊蹋?/span>OOP)的不足。 除了類(classes)以外,AOP提供了 切面。切面對(duì)關(guān)注點(diǎn)進(jìn)行模塊化,例如橫切多個(gè)類型和對(duì)象的事務(wù)管理。 (這些關(guān)注點(diǎn)術(shù)語(yǔ)通常稱作 橫切(crosscutting) 關(guān)注點(diǎn)。)
我們來(lái)定義一些重要的AOP概念。這些術(shù)語(yǔ)不是Spring特有的。不幸的是,Spring術(shù)語(yǔ)并不是特別的直觀;如果Spring使用自己的術(shù)語(yǔ),將會(huì)變得更加令人困惑。
l 切面(Aspect):一個(gè)關(guān)注點(diǎn)的模塊化,這個(gè)關(guān)注點(diǎn)可能會(huì)橫切多個(gè)對(duì)象。事務(wù)管理是J2EE應(yīng)用中一個(gè)關(guān)于橫切關(guān)注點(diǎn)的很好的例子。在Spring AOP中,切面可以使用通用類(基于模式的風(fēng)格)或者在普通類中以 @Aspect注解(@AspectJ風(fēng)格)來(lái)實(shí)現(xiàn)。
l 連接點(diǎn)(Joinpoint):在程序執(zhí)行過(guò)程中某個(gè)特定的點(diǎn),比如某方法調(diào)用的時(shí)候或者處理異常的時(shí)候。在Spring AOP中,一個(gè)連接點(diǎn)總是代表一個(gè)方法的執(zhí)行。通過(guò)聲明一個(gè)org.aspectj.lang.JoinPoint類型的參數(shù)可以使通知(Advice)的主體部分獲得連接點(diǎn)信息。
l 通知(Advice):在切面的某個(gè)特定的連接點(diǎn)(Joinpoint)上執(zhí)行的動(dòng)作。通知有各種類型,其中包括“around”、“before”和“after”等通知。通知的類型將在后面部分進(jìn)行討論。許多AOP框架,包括Spring,都是以攔截器做通知模型,并維護(hù)一個(gè)以連接點(diǎn)為中心的攔截器鏈。
l 切入點(diǎn)(Pointcut):匹配連接點(diǎn)(Joinpoint)的斷言。通知和一個(gè)切入點(diǎn)表達(dá)式關(guān)聯(lián),并在滿足這個(gè)切入點(diǎn)的連接點(diǎn)上運(yùn)行(例如,當(dāng)執(zhí)行某個(gè)特定名稱的方法時(shí))。切入點(diǎn)表達(dá)式如何和連接點(diǎn)匹配是AOP的核心:Spring缺省使用AspectJ切入點(diǎn)語(yǔ)法。
l 引入(Introduction):(也被稱為內(nèi)部類型聲明(inter-type declaration))。聲明額外的方法或者某個(gè)類型的字段。 Spring允許引入新的接口(以及一個(gè)對(duì)應(yīng)的實(shí)現(xiàn))到任何被代理的對(duì)象。例如,你可以使用一個(gè)引入來(lái)使bean實(shí)現(xiàn)IsModified接口,以便簡(jiǎn)化緩存機(jī)制。
l 目標(biāo)對(duì)象(Target Object):被一個(gè)或者多個(gè)切面(aspect)所通知(advise)的對(duì)象。也有人把它叫做被通知(advised)對(duì)象。既然Spring AOP是通過(guò)運(yùn)行時(shí)代理實(shí)現(xiàn)的,這個(gè)對(duì)象永遠(yuǎn)是一個(gè)被代理(proxied)對(duì)象。
l AOP代理(AOP Proxy): AOP框架創(chuàng)建的對(duì)象,用來(lái)實(shí)現(xiàn)切面契約(aspect contract)(包括通知方法執(zhí)行等功能)。在Spring中,AOP代理可以是JDK動(dòng)態(tài)代理或者CGLIB代理。注意:Spring 2.0最新引入的基于模式(schema-based)風(fēng)格和@AspectJ注解風(fēng)格的切面聲明,對(duì)于使用這些風(fēng)格的用戶來(lái)說(shuō),代理的創(chuàng)建是透明的。
l 織入(Weaving):把切面(aspect)連接到其它的應(yīng)用程序類型或者對(duì)象上,并創(chuàng)建一個(gè)被通知(advised)的對(duì)象。這些可以在編譯時(shí)(例如使用AspectJ編譯器),類加載時(shí)和運(yùn)行時(shí)完成。 Spring和其他純Java AOP框架一樣,在運(yùn)行時(shí)完成織入。
通知的類型:
l 前置通知(Before advice):在某連接點(diǎn)(join point)之前執(zhí)行的通知,但這個(gè)通知不能阻止連接點(diǎn)前的執(zhí)行(除非它拋出一個(gè)異常)。
l 返回后通知(After returning advice):在某連接點(diǎn)(join point)正常完成后執(zhí)行的通知:例如,一個(gè)方法沒(méi)有拋出任何異常,正常返回。
l 拋出異常后通知(After throwing advice):在方法拋出異常退出時(shí)執(zhí)行的通知。
l 后通知(After (finally) advice):當(dāng)某連接點(diǎn)退出的時(shí)候執(zhí)行的通知(不論是正常返回還是異常退出)。
l 環(huán)繞通知(Around Advice):包圍一個(gè)連接點(diǎn)(join point)的通知,如方法調(diào)用。這是最強(qiáng)大的一種通知類型。環(huán)繞通知可以在方法調(diào)用前后完成自定義的行為。它也會(huì)選擇是否繼續(xù)執(zhí)行連接點(diǎn)或直接返回它們自己的返回值或拋出異常來(lái)結(jié)束執(zhí)行。
環(huán)繞通知是最常用的一種通知類型。大部分基于攔截的AOP框架,例如Jboss,以及EJB 3里面的攔截器(后續(xù)章節(jié)我們會(huì)加以介紹),都只提供環(huán)繞通知。
跟AspectJ一樣,Spring提供所有類型的通知,我們推薦你使用盡量簡(jiǎn)單的通知類型來(lái)實(shí)現(xiàn)需要的功能。例如,如果你只是需要用一個(gè)方法的返回值來(lái)更新緩存,雖然使用環(huán)繞通知也能完成同樣的事情,但是你最好使用After returning通知而不是環(huán)繞通知。用最合適的通知類型可以使得編程模型變得簡(jiǎn)單,并且能夠避免很多潛在的錯(cuò)誤。比如,你不需要調(diào)用JoinPoint(用于Around Advice)的proceed() 方法,就不會(huì)有調(diào)用的問(wèn)題。
在Spring 2.0中,所有的通知參數(shù)都是靜態(tài)類型,因此你可以使用合適的類型(例如一個(gè)方法執(zhí)行后的返回值類型)作為通知的參數(shù)而不是使用一個(gè)對(duì)象數(shù)組。
切入點(diǎn)(pointcut)和連接點(diǎn)(join point)匹配的概念是AOP的關(guān)鍵,這使得AOP不同于其它僅僅提供攔截功能的舊技術(shù)。切入點(diǎn)使得定位通知(advice)可獨(dú)立于OO層次。例如,一個(gè)提供聲明式事務(wù)管理的around通知可以被應(yīng)用到一組橫跨多個(gè)對(duì)象中的方法上(例如服務(wù)層的所有業(yè)務(wù)操作)。
10.4 開(kāi)發(fā) Spring 2.0 AOP 應(yīng)用
Spring 2.0實(shí)現(xiàn)了兩種方式的AOP配置,一種是基于XML配置文件式的,可以用在JDK1.4上,另一種是基于@AspectJ風(fēng)格的標(biāo)注(Annotation)進(jìn)行AOP開(kāi)發(fā),可以用在JDK1.5的系統(tǒng)上。本節(jié)就對(duì)上節(jié)的應(yīng)用進(jìn)行改寫(xiě),使用Spring 2.0 AOP的方式來(lái)開(kāi)發(fā)。關(guān)于Spring AOP的資料和相關(guān)概念的詳細(xì)信息,可以閱讀Spring的中文文檔。
10.4.1使用aop 標(biāo)簽實(shí)現(xiàn)AOP
這種方式相對(duì)繁瑣,所不同的是不需要再定義ProxyFactoryBean的實(shí)例,而且自動(dòng)給相關(guān)的bean定義加入AOP功能,不在需要顯式的去訪問(wèn)代理過(guò)的另外給出名字的bean定義了。
好了,現(xiàn)在讓我們新建一個(gè)項(xiàng)目,名為Spring2_0AOP,并按照10.3節(jié)內(nèi)容設(shè)置好必要的類庫(kù)和必要的文件。那么再這個(gè)例子中,Man類的代碼不需要做任何修改。需要改的是FBI這個(gè)類,而且它也不需要再實(shí)現(xiàn)某些接口了,類的源碼如下所示:
import org.aspectj.lang.JoinPoint; /** *聯(lián)邦調(diào)查局的探員將您的所有行動(dòng)都記錄在案。 *@authorBeanSoft */ publicclass FBI { publicvoid before(JoinPoint point){ Man man = (Man)point.getTarget();
System.err.println("FBI 發(fā)現(xiàn)" + man.getName() + "正在進(jìn)行 " + point.getSignature().getName() + " 活動(dòng)。"); } } |
清單10.10 FBI類源碼
注意這個(gè)類里面的方法 before(JoinPoint),方法名可以是任意的,可以帶一個(gè)JoinPoint類型的參數(shù),也可以不帶參數(shù)直接寫(xiě)成before(),但是這個(gè)連接點(diǎn)(JoinPoint)對(duì)象帶來(lái)了所有和這次方法調(diào)用有關(guān)的信息,包括方法參數(shù),目標(biāo)對(duì)象等等,所以一般要做日志記錄的話會(huì)帶上它。
接下來(lái)是測(cè)試類的代碼,和以前的幾乎沒(méi)有任何不同,只不過(guò)現(xiàn)在直接訪問(wèn)的是man這個(gè)bean。源碼如下所示:
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; publicclass AOPTest { publicstaticvoid main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Man man = (Man) ctx.getBean("man"); man.qq(); man.mm(); } } |
清單10.11 測(cè)試類AOPTest源碼
這個(gè)類的執(zhí)行結(jié)果和上面的例子是類似的,如下所示:
log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext). log4j:WARN Please initialize the log4j system properly. FBI 發(fā)現(xiàn)張三正在進(jìn)行 qq 活動(dòng)。 我在聊QQ FBI 發(fā)現(xiàn)張三正在進(jìn)行 mm 活動(dòng)。 我在泡MM |
下面再介紹配置文件的寫(xiě)法,先看看完整的配置文件代碼:
<?xml version="1.0" encoding="UTF-8"?> <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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <bean id="fbi" class="FBI" /> <bean id="man" class="Man"> <property name="name"> <value type="java.lang.String">張三</value> </property> </bean>
<aop:config> <aop:pointcut id="manPointcut" expression="execution(* Man.*(..))" /> <aop:aspect id="beforeExample" ref="fbi"> <aop:before pointcut-ref="manPointcut" method="before" /> </aop:aspect> </aop:config> </beans> |
清單10.12 AOP XML格式配置文件源碼applicationContext.xml
將這個(gè)配置文件的代碼和清單10.8進(jìn)行對(duì)比,可以看到兩個(gè)不同:
1. 配置文件的開(kāi)頭加入了aop命名空間,如代碼中粗斜體所示。
2. 使用aop:config標(biāo)簽來(lái)定義AOP,不是使用ProxyFactoryBean來(lái)定義一個(gè)新的bean。
簡(jiǎn)要介紹一下這個(gè)配置。兩個(gè)bean的定義都沒(méi)有什么大的變化,一個(gè)是人的對(duì)象,另一個(gè)則是聯(lián)邦調(diào)查局的探員。而aop:config中定義了所有的AOP設(shè)置信息。aop:pointcut定義了一個(gè)切入點(diǎn),id給出了這個(gè)切入點(diǎn)的唯一名字,而expression定義了切入點(diǎn)的表達(dá)式,那么這個(gè)定義到底表示了什么信息呢?它的意思是表示一種場(chǎng)景,即執(zhí)行(execution)Man對(duì)象的所有方法的這種情況,這就是表達(dá)式execution(* Man.*(..))的意義所在,Man.*(..)表示Man類的所有方法。接下來(lái)呢,需要定義一個(gè)切面,用aop:aspect來(lái)定義,它的ref屬性指定了這個(gè)切面所對(duì)應(yīng)的bean定義的id,這里指向fbi這個(gè)bean類;子標(biāo)簽aop:before則指示了當(dāng)發(fā)生了名為manPointcut的切入點(diǎn)(情況)前(用pointcut-ref屬性指定,pointcut-ref=”manPointcut”),就調(diào)用名為before的方法,這個(gè)方法位于aspect里面的引用的那個(gè)bean中,這里是fbi(即ref=”fbi”)。其實(shí)Spring執(zhí)行到這里后,會(huì)自動(dòng)的把這些代碼翻譯成底層的Bean定義(后臺(tái)依然會(huì)采用ProxyFactoryBean這樣的機(jī)制),然后把對(duì)應(yīng)的獲取bean的操作直接委托給代理類,這就是為什么上文提到的測(cè)試類只需要訪問(wèn)原來(lái)的man這個(gè)bean,對(duì)應(yīng)的攔截類就會(huì)被執(zhí)行的原因。從這里看到Spring 2.0中要定義一個(gè)AOP的bean類,仍然是比較復(fù)雜的,XML文件和概念都增加了很多,需要讀者慢慢來(lái)學(xué)習(xí)和理解。
本節(jié)的詳細(xì)參考資料可以閱讀Spring參考文檔的6.3. Schema-based AOP support一節(jié)。
10.4.2使用標(biāo)注(@AspectJ)實(shí)現(xiàn)AOP
下面的文檔來(lái)自于Spring:"@AspectJ"使用了Java 5的注解,可以將切面聲明為普通的Java類。 AspectJ 5發(fā)布的 AspectJ project (http://www.eclipse.org/aspectj)中引入了這種@AspectJ風(fēng)格。 Spring 2.0 使用了和AspectJ 5一樣的注解,使用了AspectJ 提供的一個(gè)庫(kù)來(lái)做切點(diǎn)(pointcut)解析和匹配。
為了在Spring配置中使用@AspectJ aspects,你必須首先啟用Spring對(duì)基于@AspectJ aspects的配置支持,自動(dòng)代理(autoproxying)基于通知是否來(lái)自這些切面。 自動(dòng)代理是指Spring會(huì)判斷一個(gè)bean是否使用了一個(gè)或多個(gè)切面通知,并據(jù)此自動(dòng)生成相應(yīng)的代理以攔截其方法調(diào)用,并且確認(rèn)通知是否如期進(jìn)行。
通過(guò)在你的Spring的配置文件中引入下列元素來(lái)啟用Spring對(duì)@AspectJ的支持:
<aop:aspectj-autoproxy/>
也可以通過(guò)在你的application context中添加如下定義來(lái)啟用@AspectJ支持:
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />
你需要在你的應(yīng)用程序的classpath中引入兩個(gè)AspectJ庫(kù):aspectjweaver.jar和 aspectjrt.jar。我們這里用的MyEclipse,在添加Spring開(kāi)發(fā)功能時(shí)已經(jīng)自動(dòng)的加入了這些類庫(kù)文件,無(wú)需手工配置了。
定義切面Aspect:在啟用@AspectJ支持的情況下,在application context中定義的任意帶有一個(gè)@Aspect切面(擁有@Aspect注解)的bean都將被Spring自動(dòng)識(shí)別并用于配置在Spring AOP。
定義切入點(diǎn)Pointcut:現(xiàn)在通過(guò)在 @AspectJ 注解風(fēng)格的 AOP 中,一個(gè)切入點(diǎn)簽名通過(guò)一個(gè)普通的方法定義來(lái)提供,并且切入點(diǎn)表達(dá)式使用 @Pointcut 注解來(lái)表示(作為切入點(diǎn)簽名的方法必須返回 void 類型)。
好了,引用了這么些文檔,我們需要介紹這個(gè)基于標(biāo)注的新的AOP項(xiàng)目了,這個(gè)項(xiàng)目的名字是Spring2_0AOPAspectJ,如前一節(jié)所示加入了Spring核心和AOP類庫(kù)后,就可以開(kāi)發(fā)了。那么相比較10.4.1 使用aop 標(biāo)簽實(shí)現(xiàn)AOP一節(jié),這一個(gè)項(xiàng)目的代碼僅僅有兩個(gè)地方要改。首先我們要修改FBI類的源碼,加入標(biāo)注來(lái)實(shí)現(xiàn)切面和切入點(diǎn)定義,如下所示:
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; /** *聯(lián)邦調(diào)查局的探員將您的所有行動(dòng)都記錄在案。 *@authorBeanSoft */ @Aspect publicclass FBI { @Before("execution(* Man.*(..))") publicvoid before(JoinPoint point){ Man man = (Man)point.getTarget();
System.err.println("FBI 發(fā)現(xiàn)" + man.getName() + "正在進(jìn)行 " + point.getSignature().getName() + " 活動(dòng)。"); } } |
清單10.12 加入了Aspect標(biāo)注的FBI類
這個(gè)類中的@Before后面的"execution(* Man.*(..))"是切入點(diǎn)所對(duì)應(yīng)的切入點(diǎn)表達(dá)式,其意義和上一節(jié)的是一致的,仍然表示的是執(zhí)行 Man 類的所有方法時(shí)將觸發(fā)此方法的執(zhí)行。