当前位置: 首页 > article >正文

SpringAOP:对于同一个切入点,不同切面不同通知的执行顺序

目录

  • 1. 问题描述
  • 2. 结论
    • 结论1:"对于同一个切入点,同一个切面不同类型的通知的执行顺序"
    • 结论2:"对于同一个切入点,不同切面不同类型通知的执行顺序"
  • 3. 测试
    • 环境:SpringBoot 2.3.4.RELEASE
    • 测试集合1:针对结论1,单个切面类的情况。
      • 测试1:切入点正常执行完,无异常。
      • 测试2:切入点抛出异常
      • 测试3:@AfterReturning执行了注解属性returning,表示需要返回值
    • 测试集合2:针对结论2,多个切面类的情况
  • 4. 参考
  • 5. 结语:如果对大家有帮助,请点赞支持。如果有问题随时在评论中指出,感谢。

1. 问题描述

在Spring AOP中,对于同一个切入点,可能会有多个切面多种不同类型的通知共同作用于它,那么这些来自不同切面的不同类型通知,它们的执行顺序是怎样的?本文将答案分成2部分讲述。

  1. 对于同一个切入点,同一个切面不同类型的通知的执行顺序。
  2. 对于同一个切入点,不同切面不同类型通知的执行顺序。

文章后续安排:section 2直接给出结论,图文结合,更好理解。section 3讲述测试过程。section 4讲述参考来源。大家可以根据自己需要查看相应部分,想看结论可以直接看section2。

2. 结论

结论1:“对于同一个切入点,同一个切面不同类型的通知的执行顺序”

  1. 图片描述
    在这里插入图片描述
  2. 文字描述

@Around(before)
@Before
#切入点方法#
@AfterReturing/@AfterThrowing
(1. 假如有异常,执行@AfterThrowing。2. 假如没异常,
2.1 假如@AfterReturning中没有设置returning属性,@AfterReturning修饰的方法会被执行。
2.2 假如@AfterReturning中设置了returning属性,当切入点方法拥有返回值时,@AfterReturning修饰的方法会被执行,否则不执行)
@After (不管有没有异常,都执行)
@Around(after)
(1. 切入点方法抛出异常而且没有捕获,不执行。2. 切入点方法没有异常,或者异常被捕获,执行。)

结论2:“对于同一个切入点,不同切面不同类型通知的执行顺序”

  1. 图片描述
    在这里插入图片描述
  2. 文字描述

切入点方法之前,优先级越高的切面的通知,越先被执行。
切入点方法之后,优先级越高的切面的通知,越后被执行。
切面优先级可以通过在切面类上的"定义注解@Order"或者”实现Ordered接口中的getOrder()决定”,值越小,优先级越高。

3. 测试

环境:SpringBoot 2.3.4.RELEASE

测试集合1:针对结论1,单个切面类的情况。

测试1:切入点正常执行完,无异常。

  • 切面类:各种通知都有
@Component
@Aspect
public class CommonAspect1 {
    @Pointcut("execution(* cn.edu.szu.flow.control.service.UserService.*(..))")
    private void pointCut() {}

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Around(before)");
        Object val = joinPoint.proceed();
        System.out.println("Around(after)");
        return val;
    }

    @Before("pointCut()")
    public void before() {
        System.out.println("Before");
    }

    @AfterReturning(value = "pointCut()")
    public void afterReturning() {
        System.out.println("AfterReturning");
    }

    @AfterThrowing("pointCut()")
    public void afterThrowing() {
        System.out.println("AfterThrowing");
    }

    @After("pointCut()")
    public void after() {
        System.out.println("After");
    }
}
  • 切入点方法
@Service
public class UserService {
    public void addUser() {
        System.out.println("切入点方法执行......");
    }
  • 调用切入点方法
@SpringBootApplication
public class FlowControlApplication {
    public static void main(String[] args) {
        SpringApplication.run(FlowControlApplication.class, args);
    }
    @Autowired
    private UserService userService;
    @PostConstruct
    public void postConstruct() {
        // --------------调用切入点方法--------------
        userService.addUser();
    }
}
  • 结果:符合结论1
    验证结论1-1

测试2:切入点抛出异常

  • 切面类:和测试1相同
  • 切入点方法:在测试1基础上修改,让它抛出异常。
@Service
public class UserService {
    public void addUser() {
        System.out.println("切入点方法执行......");
        System.out.println("切入点方法抛出异常......");
        int i = 1/0;
    }
  • 调用切入点方法:和测试1相同
  • 结果:符合结论1。AfterThrowing执行,AfterReturning不执行,Around没有捕获异常,因此后面的不执行。
    验证结论1-2

测试3:@AfterReturning执行了注解属性returning,表示需要返回值

  • 切面类:在测试1基础上,只修改@AfterReturning
@AfterReturning(value = "pointCut()", returning = "result")
public void afterReturning(String result) {
    System.out.println("AfterReturning");
}
  • 切入点方法:不抛出异常
@Service
public class UserService {
    public void addUser() {
        System.out.println("切入点方法执行......");
    }
  • 调用切入点方法:和测试1相同
  • 结果:符合结论1。@AfterReturning不执行,因为切入点方法是void,而@AfterReturning指明了需要返回值。
    验证结论1-3

测试集合2:针对结论2,多个切面类的情况

  • 切面1:包含不同类型的通知,并使用@Order指明优先级。值越小,优先级越高。
@Order(0)
@Component
@Aspect
public class CommonAspect1 {
    @Pointcut("execution(* cn.edu.szu.flow.control.service.UserService.*(..))")
    private void pointCut() {}
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Around(before).One");
        Object val = joinPoint.proceed();
        System.out.println("Around(after).One");
        return val;
    }
    @Before("pointCut()")
    public void before() {
        System.out.println("Before.One");
    }
    @AfterReturning(value = "pointCut()")
    public void afterReturning() {
        System.out.println("AfterReturning.One");
    }
    @AfterThrowing("pointCut()")
    public void afterThrowing() {
        System.out.println("AfterThrowing.One");
    }
    @After("pointCut()")
    public void after() {
        System.out.println("After.One");
    }
}
  • 切面2:和切面1类似,包含不同类型的通知,并使用@Order指明优先级。值越小,优先级越高。
@Order(1)
@Component
@Aspect
public class CommonAspect2 {
    @Pointcut("execution(* cn.edu.szu.flow.control.service.UserService.*(..))")
    private void pointCut() {}
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Around(before).Two");
        Object val = joinPoint.proceed();
        System.out.println("Around(after).Two");
        return val;
    }
    @Before("pointCut()")
    public void before() {
        System.out.println("Before.Two");
    }
    @AfterReturning(value = "pointCut()")
    public void afterReturning() {
        System.out.println("AfterReturning.Two");
    }
    @AfterThrowing("pointCut()")
    public void afterThrowing() {
        System.out.println("AfterThrowing.Two");
    }
    @After("pointCut()")
    public void after() {
        System.out.println("After.Two");
    }
}
  • 切入点方法
@Service
public class UserService {
    public void addUser() {
        System.out.println("切入点方法执行......");
    }
  • 执行切入点方法
@SpringBootApplication
public class FlowControlApplication {
    public static void main(String[] args) {
        SpringApplication.run(FlowControlApplication.class, args);
    }
    @Autowired
    private UserService userService;
    @PostConstruct
    public void postConstruct() {
        // --------------调用切入点方法--------------
        userService.addUser();
    }
}
  • 符合结论2:在切入点方法之前,优先级高的先执行。在切入点方法之后,优先级高的后执行。
    验证结论2-1
  • 调换2个切面的优先级:让切面2的优先级更高
    降低切面1优先级
    提升切面2优先级
  • 结果:符合结论2。在切入点方法之前,优先级高的先执行。在切入点方法之后,优先级高的后执行。
    验证结论2-2

4. 参考

  1. Spring 5.3.39 docs
    本文测试使用SpringBoot2.3.4.RELEASE,内部使用的是Spring5.x,所以这里看的也是5.x的文档(Spring6.x文档关于这部分的内容和Spring5.x是一样的)。
    Spring 5.3.39参考文档
  • 参考1说:在切入点方法之前,优先级越高的通知越先执行。在切入点访问之后,优先级越高的通知越后执行。
  • 参考2说:在不同的切面中,可以通过给切面类”添加@Order”或者”实现Ordered接口”来指定切面的优先级,从而决定不同切面中通知的优先级。如果不指定,则执行顺序不可知。
  • 参考3说:在同一个切面中,不同类型的通知优先级由高到低分别是@Around, @Before, @After, @AfterReturning, @AfterThrowing。注意,结合参考1,@After和@AfterReturning, @AfterThrowing都是在切入点之后执行的,优先级越高的通知越后执行,因此执行顺序是@AfterThrowing,@AfterReturning,@After。
  • 结合参考1+参考3+上述测试 得出结论1。
  • 结合参考1+参考2+上述测试 得出结论2。
  1. 同一切面内通知的执行顺序:细节不多,但启发了我直接去看官方文档。

5. 结语:如果对大家有帮助,请点赞支持。如果有问题随时在评论中指出,感谢。


http://www.kler.cn/a/372034.html

相关文章:

  • Vuex store的基本用法
  • 「Mac畅玩鸿蒙与硬件6」鸿蒙开发环境配置篇6 - 理解鸿蒙项目结构
  • 基于springboot+vue实现的公考知识学习平台 (源码+L文+ppt)4-103
  • GiantPandaCVARM Neon Intrinsics 学习指北:从入门、进阶到学个通透
  • python爬虫抓取豆瓣数据教程
  • iOS调试真机出现的 “__llvm_profile_initialize“ 错误
  • Linux系统用户和权限
  • 云舟观测:基于eBPF监控主机的TCP网络连接
  • AI与低代码的碰撞:企业数字化转型的新引擎
  • 54页可编辑PPT | 大型集团企业数据治理解决方案
  • 基于用户体验的在线相册管理平台创新设计与实现
  • OpenAI低调发布多智能体工具Swarm:让多个智能体协同工作!
  • 论文翻译 | PROMPTING GPT-3 TO BE RELIABLE
  • Java中的数组
  • PyTorch nn.Conv2d 空洞卷积
  • Git 企业级开发模型
  • 深入理解所有权与借用——所有权模型的实用示例
  • QT 机器视觉 (3. 虚拟相机SDK、测试工具)
  • 【C语言】在线编译器——lightly
  • 什么是AdaBoost
  • EHOME视频平台EasyCVR私有化部署视频平台视频监控系统画面花屏、马赛克、拖影问题快速解决方法
  • WebSocket与Socket
  • sudo apt install jupyter-notebook安装notebook失败E: Aborting install.
  • 计算机视觉-霍夫变换直线检测实验报告
  • JMeter安装
  • Linux云计算 |【第五阶段】CLOUD-DAY4