Spring4-4 AOP配置
阿新 • • 發佈:2018-12-26
在Java社群中,最完整且最流行的AOP框架是AspectJ;而在Spring2.0以上版本中,可以使用基於AspectJ註解的方式或基於XML檔案的方式來配置AOP。
1. 基於AspectJ註解的方式配置AOP
1.1 實現步驟
第一步:額外加入AspectJ所需要的jar包,具體如下圖所示:
第二步:在Spring bean的配置檔案中,進行如下操作:
- 加入aop、bean和context的名稱空間;
- 配置自動掃描的包:如
<context:component-scan base-package="com.qiaobc.spring.aop.impl"></context:component-scan>
- 配置自動為匹配AspectJ註解的類生成代理物件:
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
.
第三步:將橫切關注點的程式碼抽象到切面類中,即需要為切面類新增@Component和@Aspect註解,並在類中宣告通知。
第四步:建立測試類並執行即可。
1.2 用AspectJ註解宣告切面
1.3 切入點表示式
AspectJ的通知註解需要藉助切入點表示式來匹配目標方法,具體說明如下:
1.4 重用切入點
1.5 附:核心測試程式碼
package com.qiaobc.spring.aop.impl;
import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 切面類:需要放入IoC容器中(@Component)、再宣告為一個切面(@Aspect)
* 優先順序:可以使用@Order(1)註解來指定切面的優先順序,值越小優先順序越高
* 或 實現Ordered介面, getOrder()方法的返回值越小, 優先順序越高
* @author qiaobc
*/
@Aspect
@Component
public class LoggingAspect {
/**
* 宣告切入點表示式:@Pointcut、該方法不需要填入其他程式碼
* 引用切入點表示式:com.qiaobc.spring.aop.impl.LoggingAspect.declareJoinPointExpression()
*/
@Pointcut("execution(public int com.qiaobc.spring.aop.impl.ArithmeticCalculator.*(..))")
private void declareJoinPointExpression(){}
/**
* 需求:在com.qiaobc.spring.aop.impl.ArithmeticCalculator介面的所有實現類的每個方法執行前列印日誌
* 前置通知:在目標方法開始之前執行,用@Before來宣告
* @param joinPoint : 用於讓通知訪問當前連線點的具體細節
*/
@Before(value = "declareJoinPointExpression()")
public void beforeMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("The method " + methodName + " begins with " + args);
}
/**
* 後置通知:在目標方法執行之後執行(無論該方法是否出現異常),用@After來宣告
* 特別注意:在後置通知中無法訪問目標方法的執行結果
* @param joinPoint : 用於讓通知訪問當前連線點的具體細節
*/
@After(value = "declareJoinPointExpression()")
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends");
}
/**
* 返回通知:在目標方法正常結束後執行的通知,其可以訪問目標方法的返回值
* @param joinPoint : 用於讓通知訪問當前連線點的具體細節
* @param result : 用於獲取目標方法的返回值
*/
@AfterReturning(value = "declareJoinPointExpression()", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends with " + result);
}
/**
* 異常通知:在目標方法丟擲異常時執行的通知
* @param joinPoint : 用於讓通知訪問當前連線點的具體細節
* @param exception : 用於獲取目標方法執行所丟擲的異常(該引數型別可以設定只有發生指定異常時才執行)
*/
@AfterThrowing(value = "declareJoinPointExpression()", throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, ArithmeticException exception) {
String methodName = joinPoint.getSignature().getName();
System.out.println("Exception in method \"" + methodName + "\" " + exception);
}
/**
* 環繞通知:類似於動態代理,其必須有返回值(目標方法的返回值)
* @param proceedingJoinPoint : 環繞通知需要攜帶ProceedingJoinPoint型別的引數,用於決定是否執行目標方法
*/
/*
@Around (value = "execution(public int com.qiaobc.spring.aop.impl.ArithmeticCalculator.*(..))")
public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint) {
Object result = null;
String methodName = proceedingJoinPoint.getSignature().getName();
List<Object> args = Arrays.asList(proceedingJoinPoint.getArgs());
// 執行目標方法
try {
// 前置通知
System.out.println("The method " + methodName + " begins with " + args);
// 執行目標方法
result = proceedingJoinPoint.proceed();
// 返回通知
System.out.println("The method " + methodName + " ends with " + result);
} catch (Throwable e) {
// 異常通知
System.out.println("Exception in method \"" + methodName + "\" " + e);
// 問題:org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type ...
throw new RuntimeException(e);
}
// 後置通知
System.out.println("The method " + methodName + " ends");
return result;
}
*/
}
2. 基於XML檔案的方式配置AOP
<?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/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置bean -->
<bean id="arithmeticCalculator" class="com.qiaobc.spring.aop.impl.xml.ArithmeticCalculatorImpl"></bean>
<!-- 配置切面bean -->
<bean id="loggingAspect" class="com.qiaobc.spring.aop.impl.xml.LoggingAspect"></bean>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切入點表示式 -->
<aop:pointcut expression="execution(* com.qiaobc.spring.aop.impl.xml.ArithmeticCalculator.*(..))" id="logging"/>
<!-- 配置切面及通知 -->
<aop:aspect ref="loggingAspect" order="1">
<aop:before method="beforeMethod" pointcut-ref="logging"/>
<aop:after method="afterMethod" pointcut-ref="logging"/>
<aop:after-returning method="afterReturning" returning="result" pointcut-ref="logging"/>
<aop:after-throwing method="afterThrowing" throwing="exception" pointcut-ref="logging"/>
</aop:aspect>
</aop:config>
</beans>