系列篇幅
- 1. Spring - IOC - 註冊組件
- 2. spring - IOC - 依賴注入
- 3. Spring - IOC - 組件掃描規則
- 4. Spring - IOC - 組件的作用域、懶加載、條件判斷
- 5. Spring - IOC - 組件的生命週期
- 6. Spring - AOP - 基本使用
前言
本文來整理一下AOP相關知識,這裏不直接介紹理論了,我們看下日常工作中如何使用
Filter、Interceptor、ControllerAdvice、AOP的關係
流程說明
- 加入依賴 spring-aspects
- 將業務邏輯組件和切面類都加入到容器中,並將切面類加入註解
@Aspect
- 在切面類上的每一個通知方法上標註通知註解,告訴Spring何時何地運行
切入點表達式
- 開啓基於註解的aop模式
@EnableAspectJAutoProxy
通知方法 | 說明 |
---|---|
前置通知(@Before) | 在目標方法運行之前運行 |
後置通知(@After) | 在目標方法正常結束/異常結束之後運行 |
返回通知(@AfterReturning) | 在目標方法正常返回之後運行 |
異常通知(@AfterThrowing) | 在目標方法出現異常之後運行 |
環繞通知(@Around) | 動態代理,手動推進目標方法運行 joinPoint.procced() |
引入依賴
lombok 推薦: Lombok 的使用
<!-- lombok 個人習慣非必須 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<!-- aop切面必須導入 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
常用方法說明
JoinPoint參數必須放在第一位
在某個類方法執行前調用
@After("execution(public int com.xm.demo.MathCalculator.*(..))")
public void logEnd(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.printf("方法:%s 結束,切面類:logEnd 方法", methodName);
}
抽離切面公共表達式,方便閱讀與封裝
@Pointcut("execution(public int com.xm.demo.MathCalculator.*(..))")
public void pointCut() {
}
/**
* 本類的公共切入表達式
*/
@Before("pointCut()")
public void logStart(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
String methodName = joinPoint.getSignature().getName();
System.out.printf("方法:%s 即將運行,參數列表:%s,切面類:logStart 方法",
methodName, Arrays.asList(args));
}
/**
* 其他類的公共切入表達式
*/
@After("com.xm.demo.LogAspects.pointCut()")
public void logEnd(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.printf("方法:%s 結束,切面類:logEnd 方法", methodName);
}
完整例子
定義功能類,等待切面日誌
public class MathCalculator {
public int div(int i, int j) {
System.out.printf("方法:MathCalculator.div(%d,%d)\n", i, j);
return i / j;
}
}
定義切面類@Aspect 一定要加,否則無效
@Aspect
public class LogAspects {
/**
* 抽取公共的切入點表達式
*/
@Pointcut("execution(public int com.xm.demo.MathCalculator.*(..))")
public void pointCut() {
}
@Before("pointCut()")
public void logStart(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
String methodName = joinPoint.getSignature().getName();
System.out.printf("方法:%s 即將運行,參數列表:%s,切面類:logStart 方法\n",
methodName, Arrays.asList(args));
}
@After("com.xm.demo.LogAspects.pointCut()")
public void logEnd(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.printf("方法:%s 結束,切面類:logEnd 方法\n", methodName);
}
@AfterReturning(value = "pointCut()", returning = "result")
public void logReturn(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.printf("方法:%s 正常返回,返回結果:%s,切面類:logReturn方法\n",
methodName, result);
}
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void logException(JoinPoint joinPoint, Exception exception) {
String methodName = joinPoint.getSignature().getName();
System.out.printf("方法:%s 異常,exception:%s,切面類:logException方法\n",
methodName, exception);
}
}
配置類 @EnableAspectJAutoProxy 一定要加,否則無效
@EnableAspectJAutoProxy
@Configuration
public class MainConfig {
/**
* 業務邏輯類加入容器中
*/
@Bean
public MathCalculator mathCalculator(){
return new MathCalculator();
}
/**
* 切面類加入到容器中
*/
@Bean
public LogAspects logAspects(){
return new LogAspects();
}
}
使用
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(MainConfig.class);
// 不實用ioc容器的話,切面是無效的
MathCalculator calculator = context.getBean(MathCalculator.class);
int div = calculator.div(4, 2);
System.out.printf("result:%d", div);
}
}
正常結果
方法:div 即將運行,參數列表:[4, 2],切面類:logStart 方法
方法:MathCalculator.div(4,2)
方法:div 結束,切面類:logEnd 方法
方法:div 正常返回,返回結果:2,切面類:logReturn方法
result:2
失敗結果
方法:div 即將運行,參數列表:[1, 0],切面類:logStart 方法
方法:MathCalculator.div(1,0)
方法:div 結束,切面類:logEnd 方法
方法:div 異常,exception:java.lang.ArithmeticException: / by zero,切面類:logException方法