Spring學(xué)習(xí)手冊(cè)(9)—— Spring AOP入門(mén)講述了AOP技術(shù)以及AOP基本概念蔫仙,最后我們了解了Spring對(duì)AOP的支持鸵赫。本文我們將以XML配置的方式來(lái)學(xué)習(xí)Spring AOP的具體使用荧止。
一、引入aop模式
如果想使用XML的方式配置AOP信息煮寡,我們需要先在XML配置文件中引入aop模式(aop schema)流强,因此我們的XML配置文件頭頭信息如下:
<?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.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- bean definitions here -->
</beans>
這樣配之后,我們就可以在xml文件里面直接引用aop
標(biāo)簽了贮尖。
二笛粘、定義一個(gè)切面
一個(gè)Spring AOP的切面(Aspect)在xml中也是一個(gè)傳統(tǒng)的bean
,而使用標(biāo)簽<aop:aspect>
定義一個(gè)切面湿硝,并且使用ref
來(lái)執(zhí)行被定義為該切面的bean薪前。
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>
如上,我們定義了一個(gè)id為aBean的bean實(shí)例关斜,然后使用<aop:aspect>
標(biāo)簽定義了一個(gè)id為myAspect的切面示括,而該切面指向aBean。
三痢畜、定義一個(gè)切點(diǎn)(pointcut)
上篇我們說(shuō)過(guò)垛膝,切點(diǎn)表達(dá)式用于匹配連接點(diǎn)(join point),然后根據(jù)配置的增強(qiáng)(Advice)方法在連接點(diǎn)運(yùn)行時(shí)選擇合適時(shí)間執(zhí)行丁稀。因?yàn)镾pring AOP目前僅支持運(yùn)行方法類(lèi)型的連接點(diǎn)吼拥,所以也可以認(rèn)為與一個(gè)切點(diǎn)匹配一個(gè)bean的執(zhí)行方法。
一個(gè)切點(diǎn)包含兩部分:
- 包含方法和名字的簽名线衫;
- 切點(diǎn)表達(dá)式:用于匹配具體的方法
因此我們使用xml定義切點(diǎn)如下所示:
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
...
</aop:aspect>
</aop:config>
<aop:pointcut>
必須在<aop:aspect>
內(nèi)部使用定義切點(diǎn)凿可,使用expression
的值指明切點(diǎn)表達(dá)式,id為該切點(diǎn)定義類(lèi)唯一標(biāo)示方便配置引用桶雀。這里并沒(méi)有定義切點(diǎn)(pointcut) 簽名,切點(diǎn)簽名定義一般在使用@AspectJ
注解方式定義切點(diǎn)時(shí)定義唬复。下面我們著重說(shuō)明下切點(diǎn)表達(dá)式的語(yǔ)法情況矗积。
支持的切點(diǎn)(pointcut)標(biāo)示
Spring AOP 支持以下AspectJ 的切點(diǎn)標(biāo)示(AspectJ pointcut designators)簡(jiǎn)稱(chēng)PCD,由于Spring AOP并沒(méi)有全部支持所有的PCD敞咧,因此若使用了不存在該列表內(nèi)的標(biāo)示則會(huì)拋出異常棘捣。
標(biāo)簽名 | 說(shuō)明 |
---|---|
execution | 匹配運(yùn)行方法的連接點(diǎn) |
within | 使匹配連接點(diǎn)限定在特定類(lèi)型 |
this | 限定匹配的連接點(diǎn)是給定類(lèi)型的實(shí)例 |
target | 限定匹配連接點(diǎn)的目標(biāo)是給定類(lèi)型的實(shí)例 |
args | 限定連接點(diǎn)的參數(shù)是給定類(lèi)型的實(shí)例 |
@target | 限定匹配的連接點(diǎn)的運(yùn)行的對(duì)象有該類(lèi)型的注解 |
@args | 運(yùn)行時(shí)傳遞的參數(shù)擁有給定類(lèi)型的注解 |
@within | 限定匹配給定指定注解類(lèi)型的連接點(diǎn) |
@annotation | 限定連接點(diǎn)擁有指定的注解 |
bean | 使得連接點(diǎn)匹配特質(zhì)的bean或bean集合 |
Tip:我們可以使用
&&
、||
休建、乍恐!
來(lái)將連接點(diǎn)表達(dá)式關(guān)聯(lián)起來(lái)评疗。
然而在XML配置中該類(lèi)字符需要轉(zhuǎn)換,因此我們可以使用更語(yǔ)義化的句子符號(hào)and
茵烈、or
百匆、not
。
例子
使用execution
例子
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
以上除ret-type-pattern
呜投、name-pattern
加匈、param-pattern
為必須外,其他皆為可選仑荐。
- 匹配所有的公有方法
execution(public * *(..)) - 匹配所有以set開(kāi)頭的方法
execution(* set*(..)) - 匹配所有AccountService接口定義的方法
execution(* com.xyz.service.AccountService.*(..)) - 匹配所有service包內(nèi)的方法
execution(* com.xyz.service..(..)) - 匹配所有service包以及子包內(nèi)的方法
execution(* com.xyz.service...(..)) - 所有在service包內(nèi)的連接點(diǎn)
within(com.xyz.service.*) - 所有service包以及子包內(nèi)的連接點(diǎn)
within(com.xyz.service..*) - 所有實(shí)現(xiàn)AccountService接口的連接點(diǎn)
this(com.xyz.service.AccountService) - 所有的目標(biāo)對(duì)象實(shí)現(xiàn)AccountService接口的連接點(diǎn)
target(com.xyz.service.AccountService) - 有一個(gè)參數(shù)并且運(yùn)行時(shí)傳入?yún)?shù)為Serializable的連接點(diǎn)
args(java.io.Serializable) - 目標(biāo)對(duì)象有@Transactional注解
@target(org.springframework.transaction.annotation.Transactional) - 定義的目標(biāo)對(duì)象類(lèi)型含有@Transactional注解
@within(org.springframework.transaction.annotation.Transactional) - 含有@Transactional注解的可運(yùn)行方法
@annotation(org.springframework.transaction.annotation.Transactional) - 含有一個(gè)參數(shù)并且在運(yùn)行時(shí)傳入的參數(shù)含有@Classified注解
@args(com.xyz.security.Classified) - bean名字定義為tradeService的連接點(diǎn)
bean(tradeService) - bean名字滿足Service命名的所有連接點(diǎn)
bean(Service)
四雕拼、定義增強(qiáng)方法(Advice)
Before advice
<aop:aspect id="beforeExample" ref="aBean">
<aop:pointcut id="dataAccessOperation"
expression="execution(* com.xyz.myapp.dao.*.*(..))"/>
<aop:before
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/>
...
</aop:aspect>
<aop:before>
使用pointcut-ref
引用一個(gè)切點(diǎn),并且指定增強(qiáng)執(zhí)行方法為doAccessCheck粘招。值的注意的是名為aBean的bean(也就是我們定義的切面)必須實(shí)現(xiàn)doAccessCheck方法啥寇。該方法會(huì)在目標(biāo)方法執(zhí)行前執(zhí)行。
After returning advice
<aop:aspect id="afterReturningExample" ref="aBean">
<aop:pointcut id="dataAccessOperation"
expression="execution(* com.xyz.myapp.dao.*.*(..))"/>
<aop:after-returning
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/>
...
</aop:aspect>
同上洒扎,該方法會(huì)在切點(diǎn)執(zhí)行正常返回后執(zhí)行辑甜。
當(dāng)然如果你需要獲取返回對(duì)象的話,你需要將配置信息改為如下所示:
<aop:aspect id="afterReturningExample" ref="aBean">
...
<aop:after-returning
pointcut-ref="dataAccessOperation"
returning="retVal"
method="doAccessCheck"/>
...
</aop:aspect>
其中doAccessCheck方法定義如下,其中參數(shù)名必須與XML里面配置相同逊笆,也就是說(shuō)必須與XML中的retVal一致栈戳。
public void doAccessCheck(Object retVal) {...}
After throwing advice
<aop:aspect id="afterThrowingExample" ref="aBean">
...
<aop:after-throwing
pointcut-ref="dataAccessOperation"
method="doRecoveryActions"/>
...
</aop:aspect>
當(dāng)然如果需要獲取拋出的異常時(shí)我們可以如下配置:
<aop:aspect id="afterThrowingExample" ref="aBean">
<aop:after-throwing
pointcut-ref="dataAccessOperation"
throwing="dataAccessEx"
method="doRecoveryActions"/>
...
</aop:aspect>
其中doRecoveryActions方法定義如下且必須有名為dataAccessEx參數(shù):
public void doRecoveryActions(DataAccessException dataAccessEx) {...}
After (finally) advice
切點(diǎn)返回后執(zhí)行如下增強(qiáng)方法(無(wú)論正常返回還是異常退出):
<aop:aspect id="afterFinallyExample" ref="aBean">
<aop:after
pointcut-ref="dataAccessOperation"
method="doReleaseLock"/>
...
</aop:aspect>
Around advice 環(huán)繞增強(qiáng)方法
該類(lèi)型的Advice環(huán)繞著連接點(diǎn),該增強(qiáng)方法可以選擇調(diào)用或者不掉用連接點(diǎn)方法难裆。
<aop:aspect id="aroundExample" ref="aBean">
<aop:around
pointcut-ref="businessService"
method="doBasicProfiling"/>
...
</aop:aspect>
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
Object retVal = pjp.proceed();
// stop stopwatch
return retVal;
}
如上所示子檀,我們使用aop:around
來(lái)定義環(huán)繞增強(qiáng),方法定義的第一個(gè)參數(shù)必須為ProceedingJoinPoint類(lèi)乃戈,在Around增強(qiáng)方法中褂痰,需要調(diào)用processed方法才會(huì)執(zhí)行真正的切點(diǎn)方法,否則則會(huì)放棄執(zhí)行切點(diǎn)方法症虑。
Advice 參數(shù)
有時(shí)我們需要獲取到目標(biāo)方法的參數(shù)信息缩歪,而Spring AOP為我們提供了方便的獲取方式,接下來(lái)我們就來(lái)了解下如何通過(guò)XML配置來(lái)獲取目標(biāo)方法的參數(shù)信息谍憔。
我們定義如下代碼
package com.aop.learn.service;
public interface StudentQueryService {
/**
* 根據(jù)姓名和年齡查詢(xún)學(xué)生信息匪蝙,
* 假設(shè)無(wú)重復(fù)現(xiàn)象
*/
Student queryStdent(String name,int age);
//...
}
package com.aop.learn.service.impl;
public class StudentQueryServiceImpl implements StudentQueryService {
public Student queryStdent(String name, int age) {
// do something
// query database or create a new object
}
}
切面例子(攔截器)
package com.aop.learn.interceptor;
public class InterceptorSample {
public Object interceptorMethod(ProceedingJoinPoint call, String name, int age) throws Throwable {
// do something
Object result = call.proceed();
//do something
}
}
配置信息如下
<!--定義bean信息-->
<bean id="aspectSample",class="com.aop.learn.interceptor.InterceptorSample">
<!--配置切面信息-->
<aop:config>
<aop:aspect ref="aspectSample">
<aop:pointcut id="pointsample"
expression="execution(* com.aop.learn.service.StudentQueryService.queryStdent(..))
and args(name, age)"/>
<aop:around pointcut-ref="pointsample"
method="interceptorMethod"/>
</aop:aspect>
</aop:config>
如上配置信息,在expression語(yǔ)句中增加了args(name,age)
,該表達(dá)式定義了變量名為name和age的參數(shù)习贫,該參數(shù)名必須和增強(qiáng)方法中名字一致逛球,這樣我們就能在增強(qiáng)方法中獲取目標(biāo)方法的參數(shù)信息了。
五苫昌、總結(jié)
本文我們主要學(xué)習(xí)了如何使用XML配置方式來(lái)完成Spring AOP的使用颤绕。文章中我們學(xué)習(xí)了很多配置標(biāo)簽,完成了切面定義、切點(diǎn)定義以及多種增強(qiáng)方法的定義奥务。至此我們已經(jīng)掌握了Spring AOP的基本知識(shí)點(diǎn)物独,可在具體的項(xiàng)目開(kāi)發(fā)中使用。不過(guò)學(xué)了那么多的知識(shí)點(diǎn)氯葬,我們還是需要一定的實(shí)踐來(lái)進(jìn)行消化吸收挡篓。接下來(lái)我們將構(gòu)造例子用來(lái)鞏固學(xué)習(xí)到的知識(shí)點(diǎn)。