一、多切面的执行顺序
1、切面中只有普通通知
BValidateAspect切面:
@Aspect
@Component
public class BValidataAspect {
@Before("com.njf.aop.utils.LogUtils.myPoint()")
public static void logStart(JoinPoint joinpoint){
Object[] args = joinpoint.getArgs();
String methodName = joinpoint.getSignature().getName();
System.out.println("[Validata前置通知]【"+ methodName +"】方法执行了,参数为【"+ Arrays.toString(args) +"】");
}
@AfterReturning(value = "com.njf.aop.utils.LogUtils.myPoint()", returning = "result")
public static void logReturn(JoinPoint joinpoint, Object result){
String methodName = joinpoint.getSignature().getName();
System.out.println("[Validata返回通知]【"+ methodName +"】方法执行完成,他的结果为是:" + result);
}
@AfterThrowing(value = "com.njf.aop.utils.LogUtils.myPoint()", throwing = "exception")
public static void logException(JoinPoint joinpoint, Exception exception){
String methodName = joinpoint.getSignature().getName();
System.out.println("[Validata异常通知]【"+ methodName +"】方法出现了异常,异常为: " + exception.getCause());
}
@After(value = "com.njf.aop.utils.LogUtils.myPoint()")
public static void logEnd(JoinPoint joinpoint){
String methodName = joinpoint.getSignature().getName();
System.out.println("[Validata后置通知]【"+ methodName +"】方法执行最终完成");
}
}
LogUtils 切面:
@Aspect
@Component
public class LogUtils {
@Pointcut("execution(public int com.njf.aop.calc.MyMathCalculator.*(int, int))")
public void myPoint(){}
@Before("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
public static void logStart(JoinPoint joinpoint){
Object[] args = joinpoint.getArgs();
String methodName = joinpoint.getSignature().getName();
System.out.println("[LogUtils前置通知]【"+ methodName +"】方法执行了,参数为【"+ Arrays.toString(args) +"】");
}
//想在目标方法正执行完毕之后
@AfterReturning(value = "execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))", returning = "result")
public static void logReturn(JoinPoint joinpoint, Object result){
String methodName = joinpoint.getSignature().getName();
System.out.println("[LogUtils返回通知]【"+ methodName +"】方法执行完成,他的结果为是:" + result);
}
@AfterThrowing(value = "execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))", throwing = "exception")
//想在目标方法出现异常时执行
public static void logException(JoinPoint joinpoint, Exception exception){
String methodName = joinpoint.getSignature().getName();
System.out.println("[LogUtils异常通知]【"+ methodName +"】方法出现了异常,异常为: " + exception.getCause());
}
//想在目标方法结束时执行
@After("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
public static void logEnd(JoinPoint joinpoint){
String methodName = joinpoint.getSignature().getName();
System.out.println("[LogUtils后置通知]【"+ methodName +"】方法执行最终完成");
}
测试运行:
可以发现是 ValidataAspect 切面先执行前置,然后 LogUtils 前置,然后继续 LogUtils 里面的后置与返回,最后是 ValidataAspect 的后置与返回。
图解:
注意:切面的顺序是按照类名的字母顺序来执行的。
2、使用注解指定切面运行顺序
(1)在同一个连接点上应用不止一个切面时,除非明确指定,否则它们的优先级是不确定的;
(2)切面的优先级可以通过实现 Ordered 接口或利用 @Order 注解指定;
(3)实现 Ordered 接口,getOrder() 方法的返回值越小,优先级越高;
(4)若使用 @Order 注解,序号出现在注解中
(5)@Order(1),定义切面作用的优先级,值越小优先级越高,默认值为int的最大值
案例:
@Component
@Aspect
@Order(0) //该切面先执行,序号越小,越先执行
public class TestAspect {
@Before(value = "execution(public int com.spring.aop.ICalc.add(int, int))")
public void beforeMethod(JoinPoint joinPoint) {
System.out.println("方法之前之前======");
}
}
@Component
@Aspect
@Order(1) //该切面后执行
public class LoggerAspect {
@Before(value = "execution(public int com.spring.aop.ICalc.add(int, int))")
public void beforeMethod(JoinPoint joinPoint) {
System.out.println("方法执行之前");
Object[] args = joinPoint.getArgs(); //获取方法的参数
String methodName = joinPoint.getSignature().getName(); //获取方法名
System.out.println("方法名:" + methodName + ",参数:" + Arrays.toString(args));
}
}
3、加入环绕通知
在 LogUtils 类中添加环绕通知,并且添加 @Order注解指定运行顺序
LogUtils类:
@Aspect
@Component
@Order(1)
public class LogUtils {
@Pointcut("execution(public int com.njf.aop.calc.MyMathCalculator.*(int, int))")
public void myPoint(){}
@Before("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
public static void logStart(JoinPoint joinpoint){
Object[] args = joinpoint.getArgs();
String methodName = joinpoint.getSignature().getName();
System.out.println("[LogUtils前置通知]【"+ methodName +"】方法执行了,参数为【"+ Arrays.toString(args) +"】");
}
//想在目标方法正执行完毕之后
@AfterReturning(value = "execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))", returning = "result")
public static void logReturn(JoinPoint joinpoint, Object result){
String methodName = joinpoint.getSignature().getName();
System.out.println("[LogUtils返回通知]【"+ methodName +"】方法执行完成,他的结果为是:" + result);
}
@AfterThrowing(value = "execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))", throwing = "exception")
//想在目标方法出现异常时执行
public static void logException(JoinPoint joinpoint, Exception exception){
String methodName = joinpoint.getSignature().getName();
System.out.println("[LogUtils异常通知]【"+ methodName +"】方法出现了异常,异常为: " + exception.getCause());
}
//想在目标方法结束时执行
@After("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
public static void logEnd(JoinPoint joinpoint){
String methodName = joinpoint.getSignature().getName();
System.out.println("[LogUtils后置通知]【"+ methodName +"】方法执行最终完成");
}
@Around("myPoint()")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().getName();
Object[] args = pjp.getArgs();
Object proceed = null;
try {
//@Before
System.out.println("【环绕前置通知】---"+ methodName +"---【方法开始】");
//就是利用反射调用目标方法即可,就是 method.invoke(obj, args)
proceed = pjp.proceed(args);
//@AfterReturning
System.out.println("【环绕返回通知】---"+ methodName +"---【方法返回,返回值 "+ proceed +"】");
} catch (Exception e) {
//@AfterThrowing
System.out.println("【环绕异常通知】---"+ methodName +"---【方法出现异常】,异常信息:" + e);
//为了让外界能知道这个异常,这个异常一定要抛出去
throw new RuntimeException(e);
} finally {
//@After
System.out.println("【环绕后置通知】---"+ methodName +"---【方法结束】");
}
//反射调用后的返回值也一定返回出去,不返回会空指针
return proceed;
}
}
BValidataAspect 类:
@Aspect
@Component
@Order(2)
public class BValidataAspect {
@Before("com.njf.aop.utils.LogUtils.myPoint()")
public static void logStart(JoinPoint joinpoint){
Object[] args = joinpoint.getArgs();
String methodName = joinpoint.getSignature().getName();
System.out.println("[Validata前置通知]【"+ methodName +"】方法执行了,参数为【"+ Arrays.toString(args) +"】");
}
@AfterReturning(value = "com.njf.aop.utils.LogUtils.myPoint()", returning = "result")
public static void logReturn(JoinPoint joinpoint, Object result){
String methodName = joinpoint.getSignature().getName();
System.out.println("[Validata返回通知]【"+ methodName +"】方法执行完成,他的结果为是:" + result);
}
@AfterThrowing(value = "com.njf.aop.utils.LogUtils.myPoint()", throwing = "exception")
public static void logException(JoinPoint joinpoint, Exception exception){
String methodName = joinpoint.getSignature().getName();
System.out.println("[Validata异常通知]【"+ methodName +"】方法出现了异常,异常为: " + exception.getCause());
}
@After(value = "com.njf.aop.utils.LogUtils.myPoint()")
public static void logEnd(JoinPoint joinpoint){
String methodName = joinpoint.getSignature().getName();
System.out.println("[Validata后置通知]【"+ methodName +"】方法执行最终完成");
}
}
测试运行:
LogUtils 类中加入了环绕通知,不会影响 BValidataAspect,环绕只是影响当前切面。
图解:
先执行各切面的前置,再执行目标方法,根据从内到外的顺序执行,(环绕只影响添加它的切面,且他在添加它的前面中优先级高)
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· 面试官:你是如何进行SQL调优的?