要在 Spring 中聲明 AspectJ 切面, 只需要在 IOC 容器中將切面聲明爲 Bean 實例. 當在 Spring IOC 容器中初始化 AspectJ 切面之後, Spring IOC 容器就會爲那些與 AspectJ 切面相匹配的 Bean 創建代理,AOP的原理其實就是利用了動態代理,將動態代理進行了封裝。
在 AspectJ 註解中, 切面只是一個帶有 @Aspect 註解的 Java 類,通知則是標註有某種註解的簡單的 Java 方法。
AspectJ 支持 5 種類型的通知註解:
@Before: 前置通知, 在方法執行之前執行
@After: 後置通知, 在方法執行之後執行
@AfterRunning: 返回通知, 在方法返回結果之後執行
@AfterThrowing: 異常通知, 在方法拋出異常之後
@Around: 環繞通知, 圍繞着方法執行
前置通知
前置通知:在目標 方法開始之前進行執行的通知。
前置通知使用 @Before 註解, 並將切入點表達式的值作爲註解值。
value屬性值:切入點表達式,匹配與之對應的目標。利用【*】可以進行那匹配不同的目標,參數只需要傳入類型即可。
方法體JoinPoint 參數:用來連接當前連接點的連接細節,一般包括方法名和參數值。【org.aspectj.lang.JoinPoint】包
@Before( value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int))")
public void brforelogging(JoinPoint joinPoint){ //注意 JoinPoint 的包
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("The brforelogging methor"+ methodName+" begin with"+args);
}
後置通知
後置通知: 在目標方法執行之後,無論是否發生異常,都進行執行的通知。
後置通知使用@After註解, 並將切入點表達式的值作爲註解值。
在後置通知中,不能訪問目標方法的執行結果。原因可能在執行過程中發生異常而無法得到結果。
// @After("execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))")
public void afterlogging(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The afterlogging method "+methodName+" end");
}
返回通知
返回通知: 在目標方法正常結束時,才執行的通知
返回通知使用@AfterReturning註解,並將切入點表達式的值作爲註解值。
返回通知可以訪問到方法的返回值。
returning屬性值:聲明該方法可以存在返回值,該屬性的值即爲用來傳入返回值的參數名稱。
方法體Object參數 :需要使用與returning同名參數名稱,用來接收方法的返回值。
@AfterReturning(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))",returning="result")
public void afterreturninglogging(JoinPoint joinPoint,Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("The afterreturninglogging method "+methodName+" end with "+result);
}
異常通知
異常通知 :在目標方法出現異常時纔會進行執行的代碼。
異常通知使用@AfterThrowing註解,並將切入點表達式的值作爲註解值。
throwing屬性:訪問連接點拋出的異常。
方法體Exception參數:用來接收連接點拋出的異常。Exception類匹配所有的異常,可以指定爲特定的異常 例如NullPointerException類等
@AfterThrowing(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))",throwing="ex")
public void afterthrowinglogging(JoinPoint joinPoint, Exception ex){
String methodName = joinPoint.getSignature().getName();
System.out.println("The afterthrowinglogging method "+methodName+" occurs excetion: "+ex);
}
環繞通知
環繞通知是所有通知類型中功能最爲強大的, 能夠全面地控制連接點。甚至可以控制是否執行連接點。類似於動態代理。
環繞通知 連接點的參數類型必須是 ProceedingJoinPoint ,它是 JoinPoint 的子接口,,允許控制何時執行, 是否執行連接點。
環繞通知中需要明確調用 ProceedingJoinPoint 的 proceed() 方法來執行被代理的方法,如果忘記這樣做就會導致通知被執行了,但目標方法沒有被執行。
環繞通知的方法需要有返回值,返回目標方法執行之後的結果, 即調用 joinPoint.proceed() 的返回值, 否則會出現空指針異常。
環繞通知雖然功能最爲強大的,但是一般我們都不使用這個。
@Around(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))")
public Object aroundlogging(ProceedingJoinPoint pjd){
Object result =null;
String methodName = pjd.getSignature().getName();
List<Object> args = Arrays.asList(pjd.getArgs());
try {
// 前置通知
System.out.println("The aroundlogging methor"+ methodName+" begin with"+args);
result = pjd.proceed();
// 返回通知
System.out.println("The aroundlogging method "+methodName+" end with "+result);
} catch (Throwable e) {
System.out.println("The aroundlogging method "+methodName+" occurs excetion: "+e);
// 異常通知
e.printStackTrace();
}
//後置通知
System.out.println("The aroundlogging method "+methodName+" end");
return result;
}
切入點表達式
在切入點表達式中我們可以利用【*】去進行模糊匹配。
最典型的就是下面的這個,我們可以根據這個進行一系列的變形。
execution(* com.wf.springaopImpl.ArithmeticCalculatorImpl.*(..))
//第一個 * 代表任意修飾符及任意返回值. 第二個 * 代表任意方法. .. 匹配任意數量的參數.
//若目標類與接口與該切面在同一個包中, 可以省略包名.
execution(public * ArithmeticCalculator.*(..))
// 匹配 ArithmeticCalculator 接口的所有公有方法.
execution (public double ArithmeticCalculator.*(..))
//匹配 ArithmeticCalculator 中返回 double 類型數值的方法
execution (public double ArithmeticCalculator.*(double, ..))
// 匹配第一個參數爲 double 類型的方法, .. 匹配任意數量任意類型的參數
切面優先級
當在同一個方法中存在多個切面時,我們可以在註解切面的類中添加新的註解@Order,可以給我們的切面進行優先級排序,@Order(index) 其中index的值越小,所對應的優先級就越高。
重用切點表達式
有時候一個切點可以有多個通知,就像上面,如果我們將切點表達式寫在每一個通知中,會有大量的重複代碼。因此我們可以重用切點表達式。
// 定義一個方法,利用註解@Pointcut用於聲明切入點達式,然後在其中引入方法名即可。
// 我們可以利用ValidateAspect.declareJoinPointExpression()進行跨類聲明【類名.方法名】。
// 甚至我們可以利用【類的全路徑.方法名】使切點表達式作用於不同的包中。
public class ValidateAspect {
@Pointcut(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int))")
public void declareJoinPointExpression(){
}
@Before("declareJoinPointExpression()")
public void brforeValidate(JoinPoint joinPoint){ //注意 JoinPoint 的包
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("----------The Validate methor"+ methodName+" begin with"+args);
}
@After("declareJoinPointExpression()")
public void afterValidate(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The afterValidate method "+methodName+" end");
}
}
基於xml的配置通知
對於沒有註解的,我們可以將對於切面的相關配置寫在我們的配置文件中。具體如下:
1、配置基本bean
2、配置切面bean
3、配置AOP
在配置AOP時,我們進行屬性配置
A、配置切面表達式
B、配置切面以及通知
注意:記得要引入aop命名空間。
<!-- 配置基本bean -->
<bean id="arithmeticCalculator" class="com.wf.springaopImplxml.ArithmeticCalculatorImpl"></bean>
<!-- 配置切面bean -->
<bean id="loggingAspect" class="com.wf.springaopImplxml.LoggingAspect"></bean>
<bean id="validateAspect" class="com.wf.springaopImplxml.ValidateAspect"></bean>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切點表達式 -->
<aop:pointcut expression="execution(public int com.wf.springaopImplxml.ArithmeticCalculatorImpl.*(int , int))"
id="pointcut"/>
<!-- 配置切面及通知 -->
<aop:aspect ref="loggingAspect" order="2">
<aop:before method="brforelogging" pointcut-ref="pointcut"/>
<aop:after method="afterlogging" pointcut-ref="pointcut"/>
<aop:after-returning method="afterreturninglogging" pointcut-ref="pointcut" returning="result"/>
<aop:after-throwing method="afterthrowinglogging" pointcut-ref="pointcut" throwing="ex"/>
<!-- <aop:around method="aroundlogging" pointcut-ref="pointcut"/> -->
</aop:aspect>
<!-- 配置切面及通知 -->
<aop:aspect ref="validateAspect" order="0">
<aop:before method="brforeValidate" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
不再單獨上傳到博客資源,不容易通過,後期會發出百度雲鏈接或者源碼Github地址:
https://github.com/wangfa1994/SpringLearning/tree/spring005