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

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);}@Autowiredprivate UserService userService;@PostConstructpublic 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);}@Autowiredprivate UserService userService;@PostConstructpublic 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.lryc.cn/news/471710.html

相关文章:

  • unique_ptr初始化
  • HelloCTF [RCE-labs] Level 8 - 文件描述和重定向
  • DEVOPS: 集群伸缩原理
  • 什么是SMO算法
  • MySQL根据.idb数据恢复脚本,做成了EXE可执行文件
  • Spring Boot面试题
  • 原生页面引入Webpack打包JS
  • 健康之路押注医药零售:毛利率下滑亏损扩大,医疗咨询人次大幅减少
  • 【人工智能-初级】第7章 聚类算法K-Means:理论讲解与代码示例
  • HOT 100 技巧题(136/169/75/31/287)
  • 什么是时间戳?怎么获取?有什么用?
  • LeetCode:459重复的子字符串
  • 【含开题报告+文档+PPT+源码】基于SSM的旅游与自然保护平台开发与实现
  • 【ANTs】医疗影像工具ANTs多种安装方式教程
  • 想要音频里的人声,怎么把音频里的人声和音乐分开?
  • python代码中通过pymobiledevice3访问iOS沙盒目录获取app日志
  • Spring AOP 使用方法总结
  • LeetCode 每日一题 2024/10/21-2024/10/27
  • 不到1500元的I卡可以玩转3A大作吗?撼与科技Intel Arc A750显卡游戏性能实
  • STK与MATLAB互联——仿真导航卫星与地面用户间距离和仰角参数
  • js面试问题笔记(一)
  • pip 和 pipx 的主要区别?
  • 4457M数字示波器
  • 【永中软件-注册/登录安全分析报告】
  • Tomcat作为web的优缺点
  • conda虚拟环境中安装cuda方法、遇到的问题
  • 【CPN TOOLS建模学习】设置变迁的属性
  • 一个简单的例子,说明Matrix类的妙用
  • 【C++】类和对象(四):析构函数
  • linux中各目录作用及介绍