1. 结论
官方文档:Spring AOP Advice
故各种通知的执行顺序:
- Spring版本5.2.7以后:
- @Around环绕通知前置操作
- @Before前置通知
- 目标方法
- @AfterReturnin返回通知或@AfterThrowing异常通知
- @After后置通知
- @Around环绕通知后置操作
- Spring版本5.2.7一千:
- @Around环绕通知前置操作
- @Before前置通知
- 目标方法
- @Around环绕通知后置操作
- @After后置通知
- @AfterReturnin返回通知或@AfterThrowing异常通知
2. 示例
java
/**
* 接口类
*/
public interface CalculatorService {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
java
import com.example.aop.service.CalculatorService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 接口实现类
*/
@Slf4j
@Component
public class CalculatorServiceImpl implements CalculatorService {
@Override
public int add(int i, int j) {
int result = i + j;
log.info("方法内部 result = {}", result);
// 为了测试效果,模拟异常信息
// int a = 1/0;
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
log.info("方法内部 result ={} ", result);
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
log.info("方法内部 result ={} ", result);
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
log.info("方法内部 result = {}", result);
return result;
}
}
切面类
java
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* 自定义日志切面类
*
* @author : Sakura
* @className : LogAspect
* @description : 日志切面类
*/
@Component // 交由IoC容器进行管理
@Aspect // 声明为切面类
@Slf4j
public class LogAspect {
// 设置切入点和通知类型
/**
* 前置通知:在被代理的目标方法前执行
* 设置前置通知 @Before(value = "切入点表达式配置切入点")
* 切入点表达式:
* 下面切入点设置为:public int CalculatorImpl 所有方法 任意参数类型
*/
@Before(value = "execution(public int com.example.aop.service.impl.CalculatorServiceImpl.*(..))")
public void beforeMethod(JoinPoint joinPoint) {
// 使用JoinPoint获取方法签名、方法参数等相关信息
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 获取方法参数
Object[] args = joinPoint.getArgs();
log.info("Logger--@Before前置通知,方法名称:{},方法参数:{}", methodName, Arrays.toString(args));
}
/**
* 返回通知:在被代理的目标方法成功结束后执行
* 设置返回通知: @AfterReturning(value = "切入点表达式配置切入点", returning = "res")
* res: 目标方法的返回结果
*/
@AfterReturning(value = "execution(public int com.example.aop.service.impl.CalculatorServiceImpl.*(..))", returning = "result")
public void AfterReturningMethod(JoinPoint joinPoint, Object result) {
// 使用JoinPoint获取方法签名、方法参数等相关信息
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 获取方法参数
Object[] args = joinPoint.getArgs();
log.info("Logger--@AfterReturning返回通知,方法名称:{},方法参数:{},返回结果:{}", methodName, Arrays.toString(args), result);
}
/**
* 后置通知:在被代理的目标方法最终结束后执行
* 设置前置通知: @After(value = "切入点表达式配置切入点")
*/
@After(value = "execution(public int com.example.aop.service.impl.CalculatorServiceImpl.*(..))")
public void afterMethod(JoinPoint joinPoint) {
// 使用JoinPoint获取方法签名、方法参数等相关信息
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 获取方法参数
Object[] args = joinPoint.getArgs();
log.info("Logger--@After后置通知,方法名称:{},方法参数:{}", methodName, Arrays.toString(args));
}
/**
* 异常通知:在被代理的目标方法异常结束后执行
* 设置异常通知: @AfterThrowing(value = "切入点表达式配置切入点", throwing = "ex")
* ex: 代指异常信息
*/
@AfterThrowing(value = "execution(public int com.example.aop.service.impl.CalculatorServiceImpl.*(..))", throwing = "ex")
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex) {
// 使用JoinPoint获取方法签名、方法参数等相关信息
// 获取方法名
String methodName = joinPoint.getSignature().getName();
log.info("Logger--@AfterThrowing异常通知,方法名称:" + methodName + ",异常信息:" + ex.getMessage());
}
/**
* 环绕通知:使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置
* 设置异常通知: @Around(value = "切入点表达式配置切入点)
* 注:可以使用JoinPoint的子类 ProceedingJoinPoint 调用目标方法
*/
@Around(value = "execution(public int com.example.aop.service.impl.CalculatorServiceImpl.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint) {
// 使用JoinPoint获取方法签名、方法参数等相关信息
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 获取方法参数
Object[] args = joinPoint.getArgs();
Object result = null;
try {
log.info("Logger--@Around环绕通知------目标方法执行之前");
// 使用JoinPoint的子类 ProceedingJoinPoint 调用目标方法
result = joinPoint.proceed();
log.info("Logger--@Around环绕通知------目标方法执行之后");
} catch (Throwable e) {
e.printStackTrace();
log.info("Logger--@Around环绕通知------目标方法出现异常执行");
} finally {
log.info("Logger--@Around环绕通知------目标方法执行完毕后执行");
}
return result;
}
}
测试:
当spring-aop版本为5.2.7.RELEASE
:
目标方法正常执行
2024-07-08 17:06:54.148 INFO 260808 --- [ main] com.example.aop.config.LogAspect : Logger--@Around环绕通知------目标方法执行之前
2024-07-08 17:06:54.148 INFO 260808 --- [ main] com.example.aop.config.LogAspect : Logger--@Before前置通知,方法名称:add,方法参数:[2, 3]
2024-07-08 17:06:54.154 INFO 260808 --- [ main] c.e.a.s.impl.CalculatorServiceImpl : 方法内部 result = 5
2024-07-08 17:06:54.154 INFO 260808 --- [ main] com.example.aop.config.LogAspect : Logger--@AfterReturning返回通知,方法名称:add,方法参数:[2, 3],返回结果:5
2024-07-08 17:06:54.154 INFO 260808 --- [ main] com.example.aop.config.LogAspect : Logger--@After后置通知,方法名称:add,方法参数:[2, 3]
2024-07-08 17:06:54.154 INFO 260808 --- [ main] com.example.aop.config.LogAspect : Logger--@Around环绕通知------目标方法执行之后
2024-07-08 17:06:54.154 INFO 260808 --- [ main] com.example.aop.config.LogAspect : Logger--@Around环绕通知------目标方法执行完毕后执行
目标方法异常执行:
2024-07-08 17:06:05.156 INFO 320568 --- [ main] com.example.aop.config.LogAspect : Logger--@Around环绕通知------目标方法执行之前
2024-07-08 17:06:05.156 INFO 320568 --- [ main] com.example.aop.config.LogAspect : Logger--@Before前置通知,方法名称:add,方法参数:[2, 3]
2024-07-08 17:06:05.162 INFO 320568 --- [ main] c.e.a.s.impl.CalculatorServiceImpl : 方法内部 result = 5
2024-07-08 17:06:05.163 INFO 320568 --- [ main] com.example.aop.config.LogAspect : Logger--@AfterThrowing异常通知,方法名称:add,异常信息:/ by zero
2024-07-08 17:06:05.163 INFO 320568 --- [ main] com.example.aop.config.LogAspect : Logger--@After后置通知,方法名称:add,方法参数:[2, 3]
2024-07-08 17:06:05.163 INFO 320568 --- [ main] com.example.aop.config.LogAspect : Logger--@Around环绕通知------目标方法出现异常执行
2024-07-08 17:06:05.163 INFO 320568 --- [ main] com.example.aop.config.LogAspect : Logger--@Around环绕通知------目标方法执行完毕后执行
java.lang.ArithmeticException: / by zero
at com.example.aop.service.impl.CalculatorServiceImpl.add(CalculatorServiceImpl.java:19)
at com.example.aop.service.impl.CalculatorServiceImpl$$FastClassBySpringCGLIB$$a70e6bfb.invoke(<generated>)
当spring-aop版本为5.2.6.RELEASE
:
目标方法正常执行
2024-07-08 17:04:28.394 INFO 330008 --- [ main] com.example.aop.config.LogAspect : Logger--@Around环绕通知------目标方法执行之前
2024-07-08 17:04:28.394 INFO 330008 --- [ main] com.example.aop.config.LogAspect : Logger--@Before前置通知,方法名称:add,方法参数:[2, 3]
2024-07-08 17:04:28.400 INFO 330008 --- [ main] c.e.a.s.impl.CalculatorServiceImpl : 方法内部 result = 5
2024-07-08 17:04:28.400 INFO 330008 --- [ main] com.example.aop.config.LogAspect : Logger--@Around环绕通知------目标方法执行之后
2024-07-08 17:04:28.400 INFO 330008 --- [ main] com.example.aop.config.LogAspect : Logger--@Around环绕通知------目标方法执行完毕后执行
2024-07-08 17:04:28.400 INFO 330008 --- [ main] com.example.aop.config.LogAspect : Logger--@After后置通知,方法名称:add,方法参数:[2, 3]
2024-07-08 17:04:28.401 INFO 330008 --- [ main] com.example.aop.config.LogAspect : Logger--@AfterReturning返回通知,方法名称:add,方法参数:[2, 3],返回结果:5
目标方法异常执行,可以看到 5.2.6.RELEASE
@Around通知导致异常被捕获了,没有触发 @AfterThrowing 通知,反而触发了 @AfterReturning 通知方法的执行;而5.2.7.RELEASE
触发 了@AfterThrowing 通知
2024-07-08 17:04:57.640 INFO 315420 --- [ main] com.example.aop.config.LogAspect : Logger--@Around环绕通知------目标方法执行之前
2024-07-08 17:04:57.640 INFO 315420 --- [ main] com.example.aop.config.LogAspect : Logger--@Before前置通知,方法名称:add,方法参数:[2, 3]
2024-07-08 17:04:57.647 INFO 315420 --- [ main] c.e.a.s.impl.CalculatorServiceImpl : 方法内部 result = 5
2024-07-08 17:04:57.647 INFO 315420 --- [ main] com.example.aop.config.LogAspect : Logger--@Around环绕通知------目标方法出现异常执行
2024-07-08 17:04:57.648 INFO 315420 --- [ main] com.example.aop.config.LogAspect : Logger--@Around环绕通知------目标方法执行完毕后执行
2024-07-08 17:04:57.648 INFO 315420 --- [ main] com.example.aop.config.LogAspect : Logger--@After后置通知,方法名称:add,方法参数:[2, 3]
2024-07-08 17:04:57.648 INFO 315420 --- [ main] com.example.aop.config.LogAspect : Logger--@AfterReturning返回通知,方法名称:add,方法参数:[2, 3],返回结果:null
java.lang.ArithmeticException: / by zero
at com.example.aop.service.impl.CalculatorServiceImpl.add(CalculatorServiceImpl.java:19)
at com.example.aop.service.impl.CalculatorServiceImpl$$FastClassBySpringCGLIB$$a70e6bfb.invoke(<generated>)