當相同的切面里的兩個增強處理需要在相同的連接點被織入時畏吓,Spring AOP將以隨機的方式來織入這兩個增強處理,沒有辦法指定他們的順序,如果一定要它們以特定的順序被織入,則可以考慮把它們壓縮到一個增強處理中莱睁,或者是把它們分別放在不同的切面,在通過切面的優(yōu)先等級來排序
如果只要訪問目標方法的參數(shù),Spring還提供了一個更加簡單的方式:我們可以在程序中使用args來綁定目標方法的參數(shù),如果args表達是中指定一個或多個參數(shù),則該切入點只匹配具有對應形參的方法芒澜,且目標方法的參數(shù)值將被出入增強處理方法仰剿。
如:
@Aspect
publicclass AccessArgsAspect {
@AfterReturning(pointcut ="execution(* cn.hb.spring.service1.*.*(..))"
+"&&args(food,time)", returning ="retValue")
publicvoid access(String food, Date time, Object retValue) {
System.out.println("目標方法中的String參數(shù)" + food);
System.out.println("目標方法的time參數(shù)" + time);
System.out.println("模擬日志記錄功能......");
System.out.println("目標參數(shù)的返回值:"+retValue);
}
}
@Component
publicclass Chinese3implements Person2 {
@Override
public String sayHello(String word) {
return word;
}
publicvoid eat(String food, Date time) {
System.out.println("我正在吃:" + food+",現(xiàn)在的時間是:" + time);
}
}
為什么目標方法的返回值是null,因為該切入點只匹配 public void eat(String food, Date time)方法痴晦。
從上面可以得出南吮,使用args表達式有如下兩個作用:
--提供更簡單的方式訪問目標方法的參數(shù);
--可用于對切入表達式增加額外限制誊酌。
除此之外部凑,使用args表達式時還可以使用如下形式:args(name,age,..),這表明增強處理方法中可通過name碧浊,age來訪問目標方法的參數(shù)涂邀,上面表達式括號中的2點,它表示可匹配更多的參數(shù)辉词,如:
public void doSomething(String name,int age)
這意味著只要目標方法第一個參數(shù)是String類型必孤,第二個參數(shù)是int則該方法就可以匹配該切入點猾骡。
定義切入點:
正如前面的FourAdviceTest程序中看到的瑞躺,這個切面類定義了4個增強處理,這4個增強處理分別指定了相同的切入點表達式兴想,這種做法顯然不符合軟件設計的原則:我們將那個切入點表達式重復了4次幢哨,如果需要該這個切入點,那么就要修改4處嫂便。
Spring AOP只支持以Spring Bean的方法執(zhí)行組作為連接點.
例如:
@Pointcut("execution(* transfer(..))")
private void anyOldTransfer(){}
一旦采用上面的代碼片段定義了名為anyOldTrandser的切入點之后捞镰,程序就可以重復使用該切入點了,甚至可以在其他切面類毙替、其他包的切面類里使用該切入點岸售,不過這取決于該方法簽名前的訪問修飾符。
切入點指示符:
正如前面的execution就是一個切入點指示符厂画,Spring AOP僅僅支持部分AspectJ切入點指示符凸丸,不僅如此Spring AOP只支持使用方法調(diào)用作為連接點,所以Spring AOP的切入點指示符僅匹配方法執(zhí)行的連接點袱院。
Spring AOP一共支持如下幾種切入點指示符:
execution:用于匹配執(zhí)行方法的連接點屎慢,是Spring AOP最主要的切入點指示符瞭稼,execution表達式的格式如下:
execution(modifies-pattern? ret-type-pattern declaring-type-parttern? name--pattern(parm-pattern) throws-pattern?)
以上打了問號的都可以省略。
上面格式中的execution是不變的腻惠,用于作為execution表達式的開頭环肘,整個表示式各個部分的解釋為:
modifies-pattern:指定方法的修飾符,支持通配符集灌,該部分可以省略悔雹。
ret-type-pattern:指定方法的返回值類型,支持通配符欣喧,可以使用“*”通配符來匹配所有返回值類型荠商。
declaring-type-parttern:指定方法所屬的類,支持通配符续誉,該部分可以省略莱没。
name--pattern:指定匹配指定方法名,支持通配符酷鸦,可以使用“*”通配符來匹配所有方法饰躲。
parm-pattern:指定方法聲明中的形參列表,支持兩個通配符:“”臼隔、“..”嘹裂,其中表示一個任意類型的參數(shù),而“..”表示零個或多個任意類型的參數(shù)摔握。
throws-pattern:指定方法聲明拋出的異常寄狼,支持通配符,該部分可以省略氨淌。
例如下面幾個execution表達式:
//匹配任意public方法的執(zhí)行泊愧。
execution(public * * (..))
//匹配任意方法名以set開始的方法。
execution(* set* (..))
//匹配AccountService里定義的任意方法的執(zhí)行盛正。
execution(* org.hb.AccountService.* (..))
//匹配Service包中任意類的任意方法的執(zhí)行删咱。
execution(* org.hb.service.*.*(..))
within:限定匹配特定類型的連接點,當使用Spring AOP的時候豪筝,只能匹配方法執(zhí)行的連接點,例如下面幾個within表達式:
//在Service包中的任意連接點痰滋。
within(* org,hb.service.*)
//在Service包或者子包的任意連接點
within(* org.hb.service..*)
this:用于限定AOP代理必須指定類型的實例,用于匹配該對象的所有連接點续崖。當使用Spring AOP的時候敲街,只能匹配方法執(zhí)行的連接點
例如:
//匹配實現(xiàn)了AccountService接口的代理對象的所有連接點
this(org.hb.service.AccountService)
target:用于限定目標對象必須是指定類型的實例,用于匹配該對象的所有連接點严望,當使用Spring AOP只匹配執(zhí)行方法的連接點多艇。例如:
//匹配實現(xiàn)了AccountService接口的目標對象的所有連接點
target(org.hb.service.AccountService)
args:用于對連接點的參數(shù)類型進行限制,要求參數(shù)類型是指定類型的實例著蟹,例如:
//匹配只接受一個參數(shù)墩蔓,且傳入的參數(shù)類型是Serializable的所有連接點
args(java.io.Serializable)
注意:該例中給出的切入點表達式與execution(* *(java.io.Serializable))不同:args版本只匹配動態(tài)運行時傳入?yún)?shù)值是Serializable類型的情況梢莽,而execution版本只匹配方法簽名只包含一個Serializable類型的參數(shù)的方法。
bean:用于指定只匹配指定Bean實例內(nèi)連接點奸披,實際上只能使用方法執(zhí)行作為連接點昏名,定義bean表達式需要傳入id或name,表示只匹配Bean實例內(nèi)連接點阵面,支持“*”通配符轻局。
例如:
/匹配tradeService Bean實例內(nèi)方法執(zhí)行的連接點
bean(tradeService)
//匹配名字以Service結(jié)尾的Bean實例內(nèi)方法執(zhí)行的連接點。
bean(*Service)
組合切入點表達式:
Spring支持3中邏輯運算符來組合切入點表達式:
&&:要求連接點同時匹配兩個切入點表達式样刷;
||:只要連接點匹配任意一個切入點表達式仑扑;
!:要求連接點不匹配指定切入點表達式置鼻。
⑤AOP 基于配置Xml文件的管理方式
除了前面介紹的基于JDK1.5的Annotation方式來定義切面镇饮、切入點和增強處理,Spring AOP也允許使用XML文件來定義管理它們箕母。
實際上储藐,使用XML定義AOP也是@AspectJ一樣的同樣需要指定相關數(shù)據(jù):配置切面、切入點嘶是、增強處理所需要的信息完全一樣钙勃,只是提供這些信息的位置不一樣而已。使用XMLA文件配置AOPd的方式有很多優(yōu)點但是也有一些缺點:
xml配置方式比@AspectJ方式有更多的限制:僅支持“singleton”切面的Bean聂喇,不能在xml中組合多個命名連接點的聲明辖源。
在Spring的配置文件中,所有的切面希太、切入點和增強處理都必須定義在<aop:config../>元素內(nèi)部克饶。<beans../>元素下可以包含多個<aop:config../>元素。
一個<aop:config../>可以包含多個pointcut跛十、advisor和aspect元素彤路,且這3個元素必須按照此順序類定義秕硝。
注意:當我們使用<aop:config../>方式進行配置時芥映,可能與Spring的自動代理方式?jīng)_突,例如我們使用BeanNameAutoProxyCreator或類似的方式顯示啟用了自動代理远豺,則它可能導致問題(例如有些增請?zhí)幚頉]有被織入)因此要么全部使用自動代理的方式奈偏,要么全部使用<aop:config../>配置方式。不要不兩者混合使用躯护。
配置切面:
定義切面使用<aop:aspect../>元素惊来,使用該元素來定義切面時,其實質(zhì)是將一個已有的Spring Bean轉(zhuǎn)換成切面Bean棺滞。
因為切面Bean可以當成一個普通的SpringBean來配置裁蚁,所以可以為該切面Bean配置依賴注入矢渊。
配置<aop:aspect../>元素時可以指定如下3個屬性:
id:定義該切面的標識名;
ref:指定以指定ref所引用的的普通Bean作為切面Bean枉证。
order:指定該切面Bean的優(yōu)先等級矮男,數(shù)字越小,等級越大室谚。
配置增強處理:
<aop:before.../>:Before增強處理
<aop:after../>:After增強處理
<aop:after-returning.../>:afterReturning增強處理
<aop:after-throwing../>:afterThrowing增強處理
<aop:around.../>:Around增強處理
上面的元素不能配置子元素毡鉴,但可以配置如下屬性:
pointcut:該屬性指定一個切入點表達式,Spring將在匹配該表達式的連接點時織入增強處理秒赤。
pointcut-ref:該屬性指定一個已經(jīng)存在的切入點的 名稱猪瞬,通常pointcut與pointcut-ref只需要使用其中的一個。
method:該屬性指定一個方法名入篮,指定切面Bean的該方法將作為增強處理陈瘦。
throwing:該屬性只對<aop:after-throwing../>起作用,用于指定一個形參名潮售,afterThrowing增強處理方法可以通過該形參訪問目標方法所拋出的異常甘晤。
returning:該屬性只對<aop:after-returning.../>起作用,用于指定一個形參名饲做,afterReturning增強處理方法可以通過該形參訪問目標方法的返回值线婚。
當定義切入點表達式時,XML文件配置方式和@AspectJ Annotation方式支持完全相同的切入點指示符盆均,一樣支持execution塞弊、within、args泪姨、this游沿、target和bean等切入點指示符。
XML配置文件方式和@AspectJ Annotation方式一樣支持組合切入點表達式肮砾,但XML配置方式不再使用簡單的&&诀黍、||、仗处!作為組合運算符眯勾,而是使用and(相當于&&)、or(||)和not(!)婆誓。
如:
Spring配置文件:
<aop:config>
<aop:aspect id="fourAdviceAspect"ref="fourAdviceBean"
order="2">
<aop:after pointcut="execution(* cn.hb.spring.lee.*.*(..))"
method="release"/>
<aop:before pointcut="execution(* cn.hb.spring.lee.*.*(..))"
method="authority"/>
<aop:after-returning pointcut="execution(* cn.hb.spring.lee.*.*(..))"
method="log"returning="obj"/>
<aop:around pointcut="execution(* cn.hb.spring.lee.*.*(..))"
method="proceedTX"/>
</aop:aspect>
</aop:config>
<aop:config>
<aop:aspectid="secondAspect"ref="secondAdviceBean"order="1">
<aop:before pointcut="execution(* cn.hb.spring.lee.*.*(..)) and args(aa)"
method="authority"/>
</aop:aspect>
</aop:config>
<!-- 定義一個普通組件Bean -->
<bean id="chinese"class="cn.hb.spring.lee.Chinese"/>
<bean id="fourAdviceBean"class="cn.hb.spring.lee.FourAdviceTest"/>
<bean id="secondAdviceBean"class="cn.hb.spring.lee.SecondAdvice"/>
//業(yè)務類:
public class Chinese implements Person {
public String sayHello(String word) {
System.out.println("sayHello方法開始執(zhí)行...");
return word;
}
public void eat(String food) {
System.out.println("我正在吃:" + food);
}
public void divide() {
int a = 5 / 0;
System.out.println("divide執(zhí)行完畢/" + a);
}
}
//切面Bean:
public class FourAdviceTest {
public Object proceedTX(ProceedingJoinPoint pre)throws Throwable {
System.out.println("Around增強處理:執(zhí)行目標方法前吃环,執(zhí)行模擬 開啟事務.......");
Object[] objs = pre.getArgs();
if (objs !=null && objs.length > 0
&& objs[0].getClass() == String.class) {
objs[0] ="被修改的參數(shù)";
}
Object obj = pre.proceed(objs);
System.out.println("Around增強處理:執(zhí)行目標方法之后,模擬結(jié) 束事務.....");
return obj +",新增加的內(nèi)容";
}
public void authority(JoinPoint jp) {
System.out.println("Before增強:模擬權限檢查");
System.out.println("Before增強:被織入增強處理的目標方法:"
+ jp.getSignature().getName());
System.out.println("Before增強:目標方法的參數(shù)為:" + Arrays.toString(jp.getArgs()));
System.out.println("Before增強:被注入增強的處理的目標對象:" + jp.getTarget());
}
public void log(JoinPoint jp, Object obj) {
System.out.println("AfterReturning增強:獲取目標方法的返回值:" + obj);
System.out.println("AfterReturning增強:模擬日志記錄功能.....");
System.out.println("AfterReturning增強:被織入增強處理的目標方法:"
+ jp.getSignature().getName());
System.out.println("AfterReturning增強:目標方法的參數(shù)為:"
+ Arrays.toString(jp.getArgs()));
System.out.println("AfterReturning增強:被注入增強的處理的目標對象:" + jp.getTarget());
}
public void release(JoinPoint jp) {
System.out.println("After增強:模擬方法結(jié)束后洋幻,資源釋放.....");
System.out.println("After增強:被織入增強處理的目標方法:"
+ jp.getSignature().getName());
System.out.println("After增強:目標方法的參數(shù)為:" + Arrays.toString(jp.getArgs()));
System.out.println("After增強:被注入增強的處理的目標對象:" + jp.getTarget());
}
}
配置切入點:
類似于@AspectJ方式郁轻,允許定義切入點來重用切入點表達式,XML配置方式也可以通過定義切入點來重用切入點表達式。
配置<aop:pointcut../>元素時通常需要指定如下兩個屬性:
id:指定該切入點的標識名好唯;
expression:指定該切入點關聯(lián)的切入點表達式竭沫。
如下面代碼:
<aop:pointcut id="myPointcut" expression="execution(* lee.*.*(..))"/>
除此之外,如果程序已經(jīng)使用Annotation定義了切入點骑篙,在<aop:pointcut ../>元素中指定切入點表達式時還有另一種用法:
<aop:pointcut expression="org.huaxia.SystemArchitecture.myPointcut()">