1.Spring對AOP的支持
- 新增了基于Schema的配置支持籽腕,為AOP專門提供了aop命名空間
- 新增了對AspectJ切點(diǎn)表達(dá)式語言的支持秕豫,
- 可以無縫地集成AspectJ
2.Java5.0注解知識快速進(jìn)階
注解是代碼的附屬信息惜浅,它遵循一個(gè)基本原則:注解不能干擾程序代碼的運(yùn)行,無論增加或刪除注解,代碼都能夠正常運(yùn)行。Java語言解釋器會忽略這些注解惭聂,而由第三方工具負(fù)責(zé)對注解進(jìn)行處理。第三方工具可以利用代碼中的注解間接控制程序代碼的運(yùn)行相恃,它們通過Java反射機(jī)制讀取注解的信息辜纲,并根據(jù)這些信息更改目標(biāo)程序的邏輯,而這正是Spring AOP對@AspectJ提供支持所采取的辦法拦耐。
3.使用@AspectJ
@AspectJ采用注解來描述切點(diǎn)和增強(qiáng)
@Aspect//通過該注解將PreGreetingAspectJ標(biāo)識為一個(gè)切面
public class PreGreetingAspectJ {
@Before("execution( * *To(..))")//定義切點(diǎn)和增強(qiáng)類型
public void before() throws Throwable {//增強(qiáng)的橫切邏輯
System.out.println("How are you! Mr");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!-- Configures the Camel Context-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" 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 id="waiter" class="com.example.NativeWaiter"/>
<bean class="com.example.beforeapsectJ.PreGreetingAspectJ"/>
<!--方法一耕腾、自動代理創(chuàng)建器,自動將@AspectJ注解切面類織入目標(biāo)Bean中-->
<!--<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>-->
<!--方法二杀糯、上面方法使用的是Annotation AspectJAutoProxy Creator能夠?qū)AspectJ注解切面類自動織入目標(biāo)Bean中扫俺,如果使用的是基于Schema的aop
命名空間進(jìn)行配置,可以使用如下方法進(jìn)行配置-->
<aop:aspectj-autoproxy/>
</beans>
4.@AspectJ語法基礎(chǔ)
4.1.切點(diǎn)表達(dá)式函數(shù)
- 方法切點(diǎn)函數(shù):通過描述目標(biāo)類方法的信息定義連接點(diǎn)
- 方法入?yún)⑶悬c(diǎn)函數(shù):通過描述目標(biāo)類方法入?yún)⒌男畔⒍x連接點(diǎn)
- 目標(biāo)類切點(diǎn)函數(shù):通過描述目標(biāo)類類型的信息定義連接點(diǎn)
- 代理類切點(diǎn)函數(shù):通過描述目標(biāo)類的代理類的信息定義連接點(diǎn)
4.2.在函數(shù)入?yún)⒅惺褂猛ㄅ浞?/h5>
- :匹配任意字符固翰,但它只能匹配上下文中的一個(gè)元素
- .. :匹配任意字符狼纬,可以匹配上下文中的多個(gè)元素,但在表示類時(shí)骂际,必須和*聯(lián)合使用畸颅,而在表示入?yún)r(shí)則單獨(dú)使用
- :表示按類型匹配指定類的所有類,必須跟在類后面方援,如com.smart.Car+。繼承或擴(kuò)展指定類的所有類涛癌,同時(shí)還包括指定類本身
4.3.邏輯運(yùn)算符
- && :與操作符犯戏,相當(dāng)于切點(diǎn)的交集運(yùn)算
- || :或操作符,相當(dāng)于切點(diǎn)的并集運(yùn)算
- 拳话! :非操作符先匪,相當(dāng)于切點(diǎn)的反集運(yùn)算
4.4.不同增強(qiáng)類型
-
@Before,前置增強(qiáng),相當(dāng)于BeforeAdvice弃衍,Before注解類擁有兩個(gè)成員
- value:該成員用于定義切點(diǎn)
- argNames:由于無法通過Java反射機(jī)制獲取方法入?yún)⒚椒牵匀绻贘ava編譯時(shí)未啟用調(diào)試信息,或者需要在運(yùn)行期解析切點(diǎn)镜盯,就必須通過這個(gè)成員指定注解所標(biāo)注增強(qiáng)方法的參數(shù)名(注意二者名字必須完全相同)岸裙,多個(gè)參數(shù)名用逗號分隔
-
@AfterReturning,后置增強(qiáng)速缆,相當(dāng)于AfterReturningAdvice降允,AfterReturning注解類擁有4個(gè)成員
- value:該成員用于定義切點(diǎn)
- pointcut:表示切點(diǎn)的信息,如果顯示指定pointcut值艺糜,那么它將覆蓋value的設(shè)置值剧董,可以將pointcut成員看做value的同義詞
- returning:將目標(biāo)對象方法的返回值綁定給增強(qiáng)的方法
- argNames:如上面所述(參數(shù)名)
-
@Around幢尚,環(huán)繞增強(qiáng),相當(dāng)于MethodInterceptor翅楼,Around注解類擁有兩個(gè)成員
- value:該成員用于定義切點(diǎn)
- argNames:如上面所述(參數(shù)名)
-
@AfterThrowing,拋出增強(qiáng)尉剩,相當(dāng)于ThrowsAdvice,AfterThrowing注解類擁有4個(gè)成員
- value:該成員用于定義切點(diǎn)
- pointcut:表示切點(diǎn)的信息毅臊,如果顯示指定pointcut值理茎,那么它將覆蓋value的設(shè)置值」游ⅲ可以將pointcut成員看作value的同義詞功蜓。
- throwing:將拋出的異常綁定到增強(qiáng)的方法中
- argNames:如上面所述(參數(shù)名)
-
@After,F(xiàn)inal增強(qiáng)宠蚂,不管是跑出異常還是正常退出式撼,該增強(qiáng)都會得到執(zhí)行,該增強(qiáng)沒有對應(yīng)的增強(qiáng)接口求厕,可以將它看成ThrowsAdvice和AfterReturningAdvice的混合物著隆,一般用于釋放資源,相當(dāng)于try{}finally{}的控制流呀癣。After注解類擁有兩個(gè)成員
- value:該成員用于定義切點(diǎn)
- argNames:如上面所述(參數(shù)名)
-
@DeclareParents,引介增強(qiáng)美浦,相當(dāng)于IntroductionInterceptor,DeclareParents注解類擁有兩個(gè)成員
- value:該成員用于定義切點(diǎn)项栏,它表示在哪個(gè)目標(biāo)類上添加引介增強(qiáng)
- defaultImpl:默認(rèn)的接口實(shí)現(xiàn)類
5.切點(diǎn)函數(shù)詳解
5.1.@annotation():表示標(biāo)注了注解的所有方法
@AspectJ
public class TestAspectJ {
@AfterReturning("@annotation(com.example.annotationAspectJ.Override)")//后置增強(qiáng)切面
public void needTestFun(){
System.out.println("needTestFun() executed!");
}
}
5.2.execution():其語法如下
execution(<修飾符模式>浦辨?<返回類型模式><方法名模式>(<參數(shù)模式>)<異常模式>?)
- 通過方法簽名定義切點(diǎn)
- execution(public (..)):匹配所有的目標(biāo)類的public方法,第一個(gè)代表返回
類型沼沈,第二個(gè)代表方法名
- execution(* TO(..)):匹配目標(biāo)類所有以To為后綴的方法流酬,第一個(gè)代表返回類
型,而*To代表任意以To為后綴的方法
- 通過類定義切點(diǎn)
- execution(* com.example.Waiter.(..)):匹配Waiter接口的所有方法列另,第一個(gè)代表返回任意類型芽腾,com.example.Waiter.*代表接口中的所有方法
- execution(* com.example.Waiter+.*):匹配Waiter接口及其所有實(shí)現(xiàn)類的方法,
- 通過類包定義切點(diǎn)
- execution(* com.example.*(..)):匹配com.example包下所有類的所有方法
- execution(* com.example..*(..)):匹配com.example包页衙、子孫包下所有類的所有方法
- execution(* com...Dao.finf*(..)):匹配包名前綴為com的任何包下類名后綴為Dao的方法摊滔,方法名必須以find為前綴
- 通過方法入?yún)⒍x切點(diǎn)
- execution(* joke(String,int)):匹配joke(String,int)方法,且joke()方法的第一個(gè)入?yún)⑹荢tring店乐,第二個(gè)入?yún)⑹莍nt艰躺,如果方法中入?yún)㈩愋褪莏ava.lang下的包可以直接使用類名;否則必須使用全限定類名
- execution(* joke(String,*)):匹配目標(biāo)類中的joke()方法眨八,該方法第一個(gè)入?yún)镾tring,第二個(gè)入?yún)⒖梢允侨我忸愋?/li>
- execution(* joke(String,..)):匹配目標(biāo)類中的joke()方法描滔,該方法的第一個(gè)入?yún)镾tring,后面可以有任意個(gè)入?yún)⑶胰雲(yún)㈩愋筒幌?/li>
- execution(* joke(Object+)):匹配目標(biāo)類中的joke()方法踪古,方法擁有一個(gè)入?yún)⒑ぃ胰雲(yún)⑹荗bject類型或該類的子類
5.3.args()和@args()
args()函數(shù)入?yún)⑹穷惷唬鳣args()函數(shù)的入?yún)⒈仨毷亲⒔忸惖念惷km然args()允許在類名后使用"+"通配符拘泞,但該通配符在此處沒有意義纷纫,添加和不添加效果都一樣
- args():該函數(shù)接收一個(gè)類名,表示目標(biāo)類方法入?yún)ο笫侵付?包含子類)時(shí)陪腌,切點(diǎn)匹配
- @args():該函數(shù)接收一個(gè)注解類的類名辱魁,當(dāng)方法的運(yùn)行時(shí)入?yún)ο髽?biāo)注了指定的注解時(shí),匹配切點(diǎn)
5.4.within()
通過類匹配模式串聲明切點(diǎn)诗鸭,within()函數(shù)定義的連接點(diǎn)事針對目標(biāo)類而言的染簇,而非針對運(yùn)行期對象而言,within()所指定的連接點(diǎn)最小范圍只能是類强岸。
- within(com.example.NativeWaiter):匹配目標(biāo)類NativeWaiter所有的方法
- within(com.example.*):匹配com.example包中所有的類
- within(com.example.**):匹配com.example包以及子孫包中的類
5.5.@within()和@target()
@within()和@target()只接受注解類名作為參數(shù)入?yún)⒍凸珸target(M)匹配任意標(biāo)注了@M目標(biāo)類,而@within(M)匹配標(biāo)注了@M的類及子孫類蝌箍,要注意青灼,如果標(biāo)注@M的是一個(gè)接口,那么@within和@target都不會起作用妓盲,因?yàn)锧within杂拨,@target以及@annotation函數(shù)都是針對目標(biāo)類而言的,而非針對運(yùn)行時(shí)引用類型而言的悯衬。
5.6.@target()和this()
target()切點(diǎn)函數(shù)通過判斷目標(biāo)類是否按類型匹配指定類來決定連接點(diǎn)是否匹配弹沽,而this()函數(shù)則通過判斷代理類是否按類型匹配指定類來決定是否和切點(diǎn)匹配
- target():target(M)表示如果目標(biāo)類按類型匹配于M,則目標(biāo)類的所有方法都匹配切點(diǎn)
- this():如果聲明一個(gè)切點(diǎn)tis(com.example.NativeWaiter),如果不使用CGLib動態(tài)代理筋粗,則生成的對象屬于Waiter類型策橘,而非NaiveWaiter(可以通過instanceof操作符來判斷),但是NaiveWaiter中所有方法都被織入了增強(qiáng)
@Aspect
public class TestAspect {
//后期增強(qiáng)亏狰,織入任何運(yùn)行期對象為Seller類型的Bean中
@AfterReturning("this(com.example.SmartSeller)")
public void thisTest(){
System.out.println("thisTest() executed()");
}
}
6.@AspectJ進(jìn)階
@AspectJ可以使用切點(diǎn)函數(shù)定義切點(diǎn),還可以使用邏輯運(yùn)算符對切點(diǎn)進(jìn)行復(fù)合運(yùn)算得到復(fù)合切點(diǎn)偶摔。
6.1.切點(diǎn)復(fù)合運(yùn)算
@Aspect
public class TestAspectJ {
@After("within(com.example.*)&&execution(* greetTo(..))")
public void greetTo(){
System.out.println("--greetToFun() executed!--");
}
@Before("!target(com.example.NativeWaiter)&&execution(* serverTo(..))")
public void notServeInNaiveWaiter(){
System.out.println("--notServeInNaiveWaiter--");
}
@AfterReturning("target(com.example.Waiter)||target(com.example.SmartSeller)")
public void waiterOrSeller(){
System.out.println("--waiterOrSell() executed!--");
}
}
6.2.命名切點(diǎn)
切點(diǎn)可以直接聲明在增強(qiáng)方法處暇唾,這種切點(diǎn)聲明方式成為匿名切點(diǎn),匿名切點(diǎn)只能在聲明處使用辰斋。如果希望在其他地方重用一個(gè)切點(diǎn)策州,則可以通過@Pointcut注解及切面類方法對切點(diǎn)進(jìn)行命名。
public class TestNamePointcut {
@Pointcut("within(com.example.*)")
public void inPackage(){}//通過注解方法inPackage()對該切點(diǎn)進(jìn)行命名宫仗,方法可視域修飾符為private够挂,表明該命名切點(diǎn)只能在本切面類中使用
@Pointcut("execution(* greetTo(..))")
protected void greetTo(){}//通過注解該方法greetTo()對該切點(diǎn)進(jìn)行命名,方法可視域修飾符protected藕夫,表明該命名切點(diǎn)可以在當(dāng)前包中的切面類孽糖,子切面類中使用
@Pointcut("inPackage() greetTo()")
public void inPkgGreetTo(){}//引用命名切點(diǎn)定義的切點(diǎn)枯冈,本切點(diǎn)也是命名切點(diǎn),它對應(yīng)的可視域?yàn)閜ublic
}
6.3.增強(qiáng)織入的順序
一個(gè)連接點(diǎn)可以同時(shí)匹配多個(gè)切點(diǎn)办悟,切點(diǎn)對應(yīng)的增強(qiáng)在連接點(diǎn)上的織入順序是如何安排呢?
- 如果增強(qiáng)在同一個(gè)切面類中聲明尘奏,則依照增強(qiáng)在切面類中定義的順序進(jìn)行織入
- 如果增強(qiáng)位于不同的切面類中,且這些切面類都實(shí)現(xiàn)了org.springframework.core.Ordered接口病蛉,則由接口方法的順序號決定(順序號小的先織入)
- 如果增強(qiáng)位于不同的切面類中炫加,且這些切面類沒有實(shí)現(xiàn)org.springframework.core.Ordered接口,則織入的順序是不確定的
6.4.訪問連接點(diǎn)信息
AspectJ使用org.aspectj.lang.JointPoint接口表示目標(biāo)類連接點(diǎn)對象铺然。如果是環(huán)繞增強(qiáng)俗孝,則使用org.aspectj.lang.ProceedingJointPoint表示連接點(diǎn)對象,該類是JointPoint的子接口魄健。任何增強(qiáng)方法都可以通過將第一個(gè)入?yún)⒙暶鳛镴ointPoint訪問連接點(diǎn)上下文信息
-
JointPoint
- java.lang.Object[].getArgs():獲取連接點(diǎn)方法運(yùn)行時(shí)的入?yún)⒘斜?/li>
- Signature getSignature():獲取連接點(diǎn)的方法簽名對象
- java.lang.Object getTarget():獲取連接點(diǎn)所在的目標(biāo)對象
- java.lang.Object getThis():獲取代理對象本身
-
ProceedingJoinPoint
- java.lang.Object proceed() throw java.lang.Throwable:通過反射執(zhí)行目標(biāo)
對象連接點(diǎn)處的方法
- java.lang.Object proceed(java.lang.Object[] args) throws java.lang.
Throwable:通過反射執(zhí)行目標(biāo)對象連接點(diǎn)處的方法赋铝,不過使用新的參數(shù)替換原來的入?yún)?/li>
@Aspect
public class TestAspectJ {
@After("within(com.example.*)&&execution(* greetTo(..))")
public void greetTo(){
System.out.println("--greetToFun() executed!--");
}
@Before("!target(com.example.NativeWaiter)&&execution(* serverTo(..))")
public void notServeInNaiveWaiter(){
System.out.println("--notServeInNaiveWaiter--");
}
@AfterReturning("target(com.example.Waiter)||target(com.example.SmartSeller)")
public void waiterOrSeller(){
System.out.println("--waiterOrSell() executed!--");
}
@Before("TestNamePointcut.inPkgGreetTo()")
public void pkgGreetTo(){//引用TestNamePointcut.inPkgGreetTo()切點(diǎn)
System.out.println("--pkgGreetTo() executed!--");
}
@Before("!target(com.example.NativeWaiter)&&TestNamePointcut.inPkgGreetTo()")
public void pkgGreetToNoNaiveWaiter(){//在復(fù)合運(yùn)算中使用了命名切點(diǎn)
System.out.println("--pkgGreetToNotNaiveWaiter() executed!--");
}
@Around("execution(* greetTo(..))&&target(com.example.NativeWaiter)")//環(huán)繞增強(qiáng)
public void joinPointAccess(ProceedingJoinPoint joinPoint) throws Throwable {//聲明連接點(diǎn)入?yún)? System.out.println("-------joinPointAccess-------");
System.out.println("args[0]"+joinPoint.getArgs()[0]);
System.out.println("signature"+joinPoint.getTarget().getClass());
joinPoint.proceed();
System.out.println("--------jointPointAccess-----------");
}
}
6.5.綁定連接點(diǎn)方法入?yún)?/h5>
在介紹切點(diǎn)函數(shù)時(shí)說過args()诀艰、this()柬甥、target()、@args()其垄、@within()苛蒲、@target()和@annotaion()這7個(gè)函數(shù)除了可以指定類名外,還可以指定參數(shù)名绿满,將目標(biāo)對象連接點(diǎn)上的方法入?yún)⒔壎ǖ皆鰪?qiáng)的方法中臂外。
- args:用于綁定連接點(diǎn)方法的入?yún)?/li>
- @annotation:用于綁定連接點(diǎn)方法的注解對象
- @args:用于綁定連接點(diǎn)方法入?yún)⒌淖⒔?/li>
/**
* 綁定連接點(diǎn)參數(shù),首先args(name,num,..)根據(jù)增強(qiáng)方法入?yún)⒄业? * name和num對應(yīng)的類型以得到真實(shí)的切點(diǎn)表達(dá)式喇颁,其次漏健,在該增強(qiáng)方
* 法織入目標(biāo)連接點(diǎn)時(shí),增強(qiáng)方法可以通過num和name訪問到連接點(diǎn)的方法入?yún)? * @param num
* @param name
*/
@Before("target(com.example.NativeWaiter)&&args(name,num,..)")
public void bindJoinPointParams(int num,String name){//增強(qiáng)方法接受連接點(diǎn)的參數(shù)
System.out.println("----bindJoinPointParams()----");
System.out.println("name:"+name);
System.out.println("num"+num);
System.out.println("------bindJoinPointParams()---");
}
6.6.綁定代理對象
使用this()或target()函數(shù)可綁定被代理對象實(shí)例橘霎,在通過類實(shí)例名綁定對象時(shí)蔫浆,依然具有原來連接點(diǎn)匹配的功能,只不過類名是通過增強(qiáng)方法中同名入?yún)⒌念愋烷g接決定罷了姐叁。
/**
* 通過2處查找出waiter對應(yīng)的Waiter瓦盛,因而切點(diǎn)表達(dá)式為this(Waiter)
* 當(dāng)增強(qiáng)方法織入目標(biāo)連接點(diǎn)時(shí),增強(qiáng)方法通過waiter入?yún)⒔壎繕?biāo)對象
* @param waiter
*/
@Before("this(waiter)")
public void bindProxyObj(Waiter waiter){//2
System.out.println("----bindProxyObj()----");
System.out.println(waiter.getClass().getName());
System.out.println("-----bindProxyObj()------");
}
6.7.綁定類注解對象
@within()和@target()函數(shù)可以將目標(biāo)類的注解對象綁定到增強(qiáng)方法中
/**
* 通過2處查找出n對應(yīng)NeedTest注解外潜,因而真實(shí)的切點(diǎn)表達(dá)式為@within(NeedTest)原环。
* 當(dāng)增強(qiáng)方法織入目標(biāo)連接點(diǎn)時(shí),增強(qiáng)方法通過n入?yún)⒖梢砸玫竭B接點(diǎn)
* @param n
*/
@Before("@within(n)")
public void bindTypeAnnoObject(NeedTest n){//2
System.out.println("----bindTypeAnnoObject()----");
System.out.println(n.getClass().getName());
System.out.println("-----bindTypeAnnoObject()------");
}
6.8.綁定返回值
在后置增強(qiáng)中处窥,可以通過returning綁定連接點(diǎn)方法的返回值
@AfterReturning(value = "target(com.example.Waiter))",returning = "retVal")
public void bindReturnValue(int retVal){
System.out.println("--bindReturnValue--");
System.out.println("returnValue"+retVal);
System.out.println("--bindReturnValue--");
}
6.9.綁定拋出的異常
和通過切點(diǎn)函數(shù)綁定連接點(diǎn)信息不同嘱吗,連接點(diǎn)拋出的異常必須使用AfterThrowing注解的throwing成員進(jìn)行綁定
@AfterThrowing(value = "target(com.example.Waiter))",throwing = "iae")
public void bindException(IllegalArgumentException iae){
System.out.println("--bindException--");
System.out.println("exception:"+iae.getMessage());
System.out.println("--bindException--");
}
7.基于Schema配置切面
7.1.一個(gè)簡單切面的配置
<aop:config proxy-target-class="true">
<aop:aspect ref="adviceMethods">
<aop:before pointcut="target(com.example.Waiter)) " method="preGreeting"/>
</aop:aspect>
</aop:config>
<bean id="naiveWaiter" class="com.example.NativeWaiter"></bean>
<bean id="adviceMethods" class="com.example.schemaAspectJ.AdviceMethods"></bean>
public class AdviceMethods {
public void preGreeting(){//該方法通過配置被用作增強(qiáng)的方法
System.out.println("--how are you!--");
}
}
7.2.配置命名切點(diǎn)
<aop:aspect ref="adviceMethods">
<aop:before pointcut="target(com.example.Waiter)) and execution(* *(..))" method="preGreeting"/>
</aop:aspect>
<!--上面定義的是匿名切點(diǎn)-->
<aop:aspect ref="adviceMethods">
<aop:pointcut id="greetpointcut" expression="target(com.example.Waiter)) and execution(* *(..))"/>
<aop:before pointcut-ref="greetpointcut" method="preGreeting"/>
</aop:aspect>
<!--命名式切點(diǎn)的配置-->
8.混合切面類型
- 基于@AspectJ注解方式
- 基于<aop:aspect>的方式
- 基于<aop:advisor>的方式
- 基于Advisor類的方式
- :匹配任意字符固翰,但它只能匹配上下文中的一個(gè)元素
- :表示按類型匹配指定類的所有類,必須跟在類后面方援,如com.smart.Car+。繼承或擴(kuò)展指定類的所有類涛癌,同時(shí)還包括指定類本身
@Before,前置增強(qiáng),相當(dāng)于BeforeAdvice弃衍,Before注解類擁有兩個(gè)成員
- value:該成員用于定義切點(diǎn)
- argNames:由于無法通過Java反射機(jī)制獲取方法入?yún)⒚椒牵匀绻贘ava編譯時(shí)未啟用調(diào)試信息,或者需要在運(yùn)行期解析切點(diǎn)镜盯,就必須通過這個(gè)成員指定注解所標(biāo)注增強(qiáng)方法的參數(shù)名(注意二者名字必須完全相同)岸裙,多個(gè)參數(shù)名用逗號分隔
@AfterReturning,后置增強(qiáng)速缆,相當(dāng)于AfterReturningAdvice降允,AfterReturning注解類擁有4個(gè)成員
- value:該成員用于定義切點(diǎn)
- pointcut:表示切點(diǎn)的信息,如果顯示指定pointcut值艺糜,那么它將覆蓋value的設(shè)置值剧董,可以將pointcut成員看做value的同義詞
- returning:將目標(biāo)對象方法的返回值綁定給增強(qiáng)的方法
- argNames:如上面所述(參數(shù)名)
@Around幢尚,環(huán)繞增強(qiáng),相當(dāng)于MethodInterceptor翅楼,Around注解類擁有兩個(gè)成員
- value:該成員用于定義切點(diǎn)
- argNames:如上面所述(參數(shù)名)
@AfterThrowing,拋出增強(qiáng)尉剩,相當(dāng)于ThrowsAdvice,AfterThrowing注解類擁有4個(gè)成員
- value:該成員用于定義切點(diǎn)
- pointcut:表示切點(diǎn)的信息毅臊,如果顯示指定pointcut值理茎,那么它將覆蓋value的設(shè)置值」游ⅲ可以將pointcut成員看作value的同義詞功蜓。
- throwing:將拋出的異常綁定到增強(qiáng)的方法中
- argNames:如上面所述(參數(shù)名)
@After,F(xiàn)inal增強(qiáng)宠蚂,不管是跑出異常還是正常退出式撼,該增強(qiáng)都會得到執(zhí)行,該增強(qiáng)沒有對應(yīng)的增強(qiáng)接口求厕,可以將它看成ThrowsAdvice和AfterReturningAdvice的混合物著隆,一般用于釋放資源,相當(dāng)于try{}finally{}的控制流呀癣。After注解類擁有兩個(gè)成員
- value:該成員用于定義切點(diǎn)
- argNames:如上面所述(參數(shù)名)
@DeclareParents,引介增強(qiáng)美浦,相當(dāng)于IntroductionInterceptor,DeclareParents注解類擁有兩個(gè)成員
- value:該成員用于定義切點(diǎn)项栏,它表示在哪個(gè)目標(biāo)類上添加引介增強(qiáng)
- defaultImpl:默認(rèn)的接口實(shí)現(xiàn)類
@AspectJ
public class TestAspectJ {
@AfterReturning("@annotation(com.example.annotationAspectJ.Override)")//后置增強(qiáng)切面
public void needTestFun(){
System.out.println("needTestFun() executed!");
}
}
execution(<修飾符模式>浦辨?<返回類型模式><方法名模式>(<參數(shù)模式>)<異常模式>?)
- execution(public (..)):匹配所有的目標(biāo)類的public方法,第一個(gè)代表返回
類型沼沈,第二個(gè)代表方法名 - execution(* TO(..)):匹配目標(biāo)類所有以To為后綴的方法流酬,第一個(gè)代表返回類
型,而*To代表任意以To為后綴的方法
- execution(* com.example.Waiter.(..)):匹配Waiter接口的所有方法列另,第一個(gè)代表返回任意類型芽腾,com.example.Waiter.*代表接口中的所有方法
- execution(* com.example.Waiter+.*):匹配Waiter接口及其所有實(shí)現(xiàn)類的方法,
- execution(* com.example.*(..)):匹配com.example包下所有類的所有方法
- execution(* com.example..*(..)):匹配com.example包页衙、子孫包下所有類的所有方法
- execution(* com...Dao.finf*(..)):匹配包名前綴為com的任何包下類名后綴為Dao的方法摊滔,方法名必須以find為前綴
- execution(* joke(String,int)):匹配joke(String,int)方法,且joke()方法的第一個(gè)入?yún)⑹荢tring店乐,第二個(gè)入?yún)⑹莍nt艰躺,如果方法中入?yún)㈩愋褪莏ava.lang下的包可以直接使用類名;否則必須使用全限定類名
- execution(* joke(String,*)):匹配目標(biāo)類中的joke()方法眨八,該方法第一個(gè)入?yún)镾tring,第二個(gè)入?yún)⒖梢允侨我忸愋?/li>
- execution(* joke(String,..)):匹配目標(biāo)類中的joke()方法描滔,該方法的第一個(gè)入?yún)镾tring,后面可以有任意個(gè)入?yún)⑶胰雲(yún)㈩愋筒幌?/li>
- execution(* joke(Object+)):匹配目標(biāo)類中的joke()方法踪古,方法擁有一個(gè)入?yún)⒑ぃ胰雲(yún)⑹荗bject類型或該類的子類
args()函數(shù)入?yún)⑹穷惷唬鳣args()函數(shù)的入?yún)⒈仨毷亲⒔忸惖念惷km然args()允許在類名后使用"+"通配符拘泞,但該通配符在此處沒有意義纷纫,添加和不添加效果都一樣
通過類匹配模式串聲明切點(diǎn)诗鸭,within()函數(shù)定義的連接點(diǎn)事針對目標(biāo)類而言的染簇,而非針對運(yùn)行期對象而言,within()所指定的連接點(diǎn)最小范圍只能是類强岸。
@within()和@target()只接受注解類名作為參數(shù)入?yún)⒍凸珸target(M)匹配任意標(biāo)注了@M目標(biāo)類,而@within(M)匹配標(biāo)注了@M的類及子孫類蝌箍,要注意青灼,如果標(biāo)注@M的是一個(gè)接口,那么@within和@target都不會起作用妓盲,因?yàn)锧within杂拨,@target以及@annotation函數(shù)都是針對目標(biāo)類而言的,而非針對運(yùn)行時(shí)引用類型而言的悯衬。
target()切點(diǎn)函數(shù)通過判斷目標(biāo)類是否按類型匹配指定類來決定連接點(diǎn)是否匹配弹沽,而this()函數(shù)則通過判斷代理類是否按類型匹配指定類來決定是否和切點(diǎn)匹配
@Aspect
public class TestAspect {
//后期增強(qiáng)亏狰,織入任何運(yùn)行期對象為Seller類型的Bean中
@AfterReturning("this(com.example.SmartSeller)")
public void thisTest(){
System.out.println("thisTest() executed()");
}
}
@AspectJ可以使用切點(diǎn)函數(shù)定義切點(diǎn),還可以使用邏輯運(yùn)算符對切點(diǎn)進(jìn)行復(fù)合運(yùn)算得到復(fù)合切點(diǎn)偶摔。
@Aspect
public class TestAspectJ {
@After("within(com.example.*)&&execution(* greetTo(..))")
public void greetTo(){
System.out.println("--greetToFun() executed!--");
}
@Before("!target(com.example.NativeWaiter)&&execution(* serverTo(..))")
public void notServeInNaiveWaiter(){
System.out.println("--notServeInNaiveWaiter--");
}
@AfterReturning("target(com.example.Waiter)||target(com.example.SmartSeller)")
public void waiterOrSeller(){
System.out.println("--waiterOrSell() executed!--");
}
}
切點(diǎn)可以直接聲明在增強(qiáng)方法處暇唾,這種切點(diǎn)聲明方式成為匿名切點(diǎn),匿名切點(diǎn)只能在聲明處使用辰斋。如果希望在其他地方重用一個(gè)切點(diǎn)策州,則可以通過@Pointcut注解及切面類方法對切點(diǎn)進(jìn)行命名。
public class TestNamePointcut {
@Pointcut("within(com.example.*)")
public void inPackage(){}//通過注解方法inPackage()對該切點(diǎn)進(jìn)行命名宫仗,方法可視域修飾符為private够挂,表明該命名切點(diǎn)只能在本切面類中使用
@Pointcut("execution(* greetTo(..))")
protected void greetTo(){}//通過注解該方法greetTo()對該切點(diǎn)進(jìn)行命名,方法可視域修飾符protected藕夫,表明該命名切點(diǎn)可以在當(dāng)前包中的切面類孽糖,子切面類中使用
@Pointcut("inPackage() greetTo()")
public void inPkgGreetTo(){}//引用命名切點(diǎn)定義的切點(diǎn)枯冈,本切點(diǎn)也是命名切點(diǎn),它對應(yīng)的可視域?yàn)閜ublic
}
一個(gè)連接點(diǎn)可以同時(shí)匹配多個(gè)切點(diǎn)办悟,切點(diǎn)對應(yīng)的增強(qiáng)在連接點(diǎn)上的織入順序是如何安排呢?
AspectJ使用org.aspectj.lang.JointPoint接口表示目標(biāo)類連接點(diǎn)對象铺然。如果是環(huán)繞增強(qiáng)俗孝,則使用org.aspectj.lang.ProceedingJointPoint表示連接點(diǎn)對象,該類是JointPoint的子接口魄健。任何增強(qiáng)方法都可以通過將第一個(gè)入?yún)⒙暶鳛镴ointPoint訪問連接點(diǎn)上下文信息
JointPoint
- java.lang.Object[].getArgs():獲取連接點(diǎn)方法運(yùn)行時(shí)的入?yún)⒘斜?/li>
- Signature getSignature():獲取連接點(diǎn)的方法簽名對象
- java.lang.Object getTarget():獲取連接點(diǎn)所在的目標(biāo)對象
- java.lang.Object getThis():獲取代理對象本身
ProceedingJoinPoint
- java.lang.Object proceed() throw java.lang.Throwable:通過反射執(zhí)行目標(biāo)
對象連接點(diǎn)處的方法 - java.lang.Object proceed(java.lang.Object[] args) throws java.lang.
Throwable:通過反射執(zhí)行目標(biāo)對象連接點(diǎn)處的方法赋铝,不過使用新的參數(shù)替換原來的入?yún)?/li>
@Aspect
public class TestAspectJ {
@After("within(com.example.*)&&execution(* greetTo(..))")
public void greetTo(){
System.out.println("--greetToFun() executed!--");
}
@Before("!target(com.example.NativeWaiter)&&execution(* serverTo(..))")
public void notServeInNaiveWaiter(){
System.out.println("--notServeInNaiveWaiter--");
}
@AfterReturning("target(com.example.Waiter)||target(com.example.SmartSeller)")
public void waiterOrSeller(){
System.out.println("--waiterOrSell() executed!--");
}
@Before("TestNamePointcut.inPkgGreetTo()")
public void pkgGreetTo(){//引用TestNamePointcut.inPkgGreetTo()切點(diǎn)
System.out.println("--pkgGreetTo() executed!--");
}
@Before("!target(com.example.NativeWaiter)&&TestNamePointcut.inPkgGreetTo()")
public void pkgGreetToNoNaiveWaiter(){//在復(fù)合運(yùn)算中使用了命名切點(diǎn)
System.out.println("--pkgGreetToNotNaiveWaiter() executed!--");
}
@Around("execution(* greetTo(..))&&target(com.example.NativeWaiter)")//環(huán)繞增強(qiáng)
public void joinPointAccess(ProceedingJoinPoint joinPoint) throws Throwable {//聲明連接點(diǎn)入?yún)? System.out.println("-------joinPointAccess-------");
System.out.println("args[0]"+joinPoint.getArgs()[0]);
System.out.println("signature"+joinPoint.getTarget().getClass());
joinPoint.proceed();
System.out.println("--------jointPointAccess-----------");
}
}
在介紹切點(diǎn)函數(shù)時(shí)說過args()诀艰、this()柬甥、target()、@args()其垄、@within()苛蒲、@target()和@annotaion()這7個(gè)函數(shù)除了可以指定類名外,還可以指定參數(shù)名绿满,將目標(biāo)對象連接點(diǎn)上的方法入?yún)⒔壎ǖ皆鰪?qiáng)的方法中臂外。
- args:用于綁定連接點(diǎn)方法的入?yún)?/li>
- @annotation:用于綁定連接點(diǎn)方法的注解對象
- @args:用于綁定連接點(diǎn)方法入?yún)⒌淖⒔?/li>
/**
* 綁定連接點(diǎn)參數(shù),首先args(name,num,..)根據(jù)增強(qiáng)方法入?yún)⒄业? * name和num對應(yīng)的類型以得到真實(shí)的切點(diǎn)表達(dá)式喇颁,其次漏健,在該增強(qiáng)方
* 法織入目標(biāo)連接點(diǎn)時(shí),增強(qiáng)方法可以通過num和name訪問到連接點(diǎn)的方法入?yún)? * @param num
* @param name
*/
@Before("target(com.example.NativeWaiter)&&args(name,num,..)")
public void bindJoinPointParams(int num,String name){//增強(qiáng)方法接受連接點(diǎn)的參數(shù)
System.out.println("----bindJoinPointParams()----");
System.out.println("name:"+name);
System.out.println("num"+num);
System.out.println("------bindJoinPointParams()---");
}
6.6.綁定代理對象
使用this()或target()函數(shù)可綁定被代理對象實(shí)例橘霎,在通過類實(shí)例名綁定對象時(shí)蔫浆,依然具有原來連接點(diǎn)匹配的功能,只不過類名是通過增強(qiáng)方法中同名入?yún)⒌念愋烷g接決定罷了姐叁。
/**
* 通過2處查找出waiter對應(yīng)的Waiter瓦盛,因而切點(diǎn)表達(dá)式為this(Waiter)
* 當(dāng)增強(qiáng)方法織入目標(biāo)連接點(diǎn)時(shí),增強(qiáng)方法通過waiter入?yún)⒔壎繕?biāo)對象
* @param waiter
*/
@Before("this(waiter)")
public void bindProxyObj(Waiter waiter){//2
System.out.println("----bindProxyObj()----");
System.out.println(waiter.getClass().getName());
System.out.println("-----bindProxyObj()------");
}
6.7.綁定類注解對象
@within()和@target()函數(shù)可以將目標(biāo)類的注解對象綁定到增強(qiáng)方法中
/**
* 通過2處查找出n對應(yīng)NeedTest注解外潜,因而真實(shí)的切點(diǎn)表達(dá)式為@within(NeedTest)原环。
* 當(dāng)增強(qiáng)方法織入目標(biāo)連接點(diǎn)時(shí),增強(qiáng)方法通過n入?yún)⒖梢砸玫竭B接點(diǎn)
* @param n
*/
@Before("@within(n)")
public void bindTypeAnnoObject(NeedTest n){//2
System.out.println("----bindTypeAnnoObject()----");
System.out.println(n.getClass().getName());
System.out.println("-----bindTypeAnnoObject()------");
}
6.8.綁定返回值
在后置增強(qiáng)中处窥,可以通過returning綁定連接點(diǎn)方法的返回值
@AfterReturning(value = "target(com.example.Waiter))",returning = "retVal")
public void bindReturnValue(int retVal){
System.out.println("--bindReturnValue--");
System.out.println("returnValue"+retVal);
System.out.println("--bindReturnValue--");
}
6.9.綁定拋出的異常
和通過切點(diǎn)函數(shù)綁定連接點(diǎn)信息不同嘱吗,連接點(diǎn)拋出的異常必須使用AfterThrowing注解的throwing成員進(jìn)行綁定
@AfterThrowing(value = "target(com.example.Waiter))",throwing = "iae")
public void bindException(IllegalArgumentException iae){
System.out.println("--bindException--");
System.out.println("exception:"+iae.getMessage());
System.out.println("--bindException--");
}
7.基于Schema配置切面
7.1.一個(gè)簡單切面的配置
<aop:config proxy-target-class="true">
<aop:aspect ref="adviceMethods">
<aop:before pointcut="target(com.example.Waiter)) " method="preGreeting"/>
</aop:aspect>
</aop:config>
<bean id="naiveWaiter" class="com.example.NativeWaiter"></bean>
<bean id="adviceMethods" class="com.example.schemaAspectJ.AdviceMethods"></bean>
public class AdviceMethods {
public void preGreeting(){//該方法通過配置被用作增強(qiáng)的方法
System.out.println("--how are you!--");
}
}
7.2.配置命名切點(diǎn)
<aop:aspect ref="adviceMethods">
<aop:before pointcut="target(com.example.Waiter)) and execution(* *(..))" method="preGreeting"/>
</aop:aspect>
<!--上面定義的是匿名切點(diǎn)-->
<aop:aspect ref="adviceMethods">
<aop:pointcut id="greetpointcut" expression="target(com.example.Waiter)) and execution(* *(..))"/>
<aop:before pointcut-ref="greetpointcut" method="preGreeting"/>
</aop:aspect>
<!--命名式切點(diǎn)的配置-->
8.混合切面類型
- 基于@AspectJ注解方式
- 基于<aop:aspect>的方式
- 基于<aop:advisor>的方式
- 基于Advisor類的方式