spring 多个切面的执行顺序及原理

2023-11-13

最近和同事聊起来了springAOP的话题,说了到多个切面的时候程序是怎么执行的,我们常用的spring事务本身也是一个切面,使用的AOP原理,本人从网上找了一些资料,然后根据这些资料进行一下总结。

资料地址:1.https://blog.csdn.net/stpice/article/details/102733782

                          2.https://www.jb51.net/article/179078.htm

另外,如果有些朋友不是很清楚aop原理,可以去找我前面写的博客,讲解的简单易懂

正文:

配置AOP执行顺序的三种方式:

通过实现org.springframework.core.Ordered接口

1

2

3

4

5

6

7

8

9

10

@Component

@Aspect

@Slf4j

public class MessageQueueAopAspect1 implements Ordered{@Override

  public int getOrder() {

    // TODO Auto-generated method stub

    return 2;

  }

    

}

通过注解

1

2

3

4

5

6

7

8

@Component

@Aspect

@Slf4j

@Order(1)

public class MessageQueueAopAspect1{

    

  ...

}

通过配置文件配置

1

2

3

4

5

6

<aop:config expose-proxy="true">

  <aop:aspect ref="aopBean" order="0"

    <aop:pointcut id="testPointcut" expression="@annotation(xxx.xxx.xxx.annotation.xxx)"/> 

    <aop:around pointcut-ref="testPointcut" method="doAround" /> 

    </aop:aspect

</aop:config>

我们在同一个方法上加以下两个AOP,看看究竟。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

@Component

@Aspect

@Slf4j

public class MessageQueueAopAspect1 implements Ordered{

    

  @Resource(name="actionMessageProducer")

  private IProducer<MessageQueueInfo> actionProducer;  

    

  @Pointcut("@annotation(com.xxx.annotation.MessageQueueRequire1)")

  private void pointCutMethod() {

  }

    

  //声明前置通知

  @Before("pointCutMethod()")

  public void doBefore(JoinPoint point) {

    log.info("MessageQueueAopAspect1:doBefore");

    return;

  }

  

  //声明后置通知

  @AfterReturning(pointcut = "pointCutMethod()", returning = "returnValue")

  public void doAfterReturning(JoinPoint point,Object returnValue) {

    log.info("MessageQueueAopAspect1:doAfterReturning");

  }

  

  //声明例外通知

  @AfterThrowing(pointcut = "pointCutMethod()", throwing = "e")

  public void doAfterThrowing(Exception e) {

    log.info("MessageQueueAopAspect1:doAfterThrowing");

  }

  

  //声明最终通知

  @After("pointCutMethod()")

  public void doAfter() {

    log.info("MessageQueueAopAspect1:doAfter");

  }

  

  //声明环绕通知

  @Around("pointCutMethod()")

  public Object doAround(ProceedingJoinPoint pjp) throws Throwable {

    log.info("MessageQueueAopAspect1:doAround-1");

    Object obj = pjp.proceed();

    log.info("MessageQueueAopAspect1:doAround-2");

    return obj;

  }

    

  @Override

  public int getOrder() {

    return 1001;

  }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

@Component

@Aspect

@Slf4j

public class MessageQueueAopAspect2 implements Ordered{

    

  @Resource(name="actionMessageProducer")

  private IProducer<MessageQueueInfo> actionProducer;  

    

  @Pointcut("@annotation(com.xxx.annotation.MessageQueueRequire2)")

  private void pointCutMethod() {

  }

    

    

  //声明前置通知

  @Before("pointCutMethod()")

  public void doBefore(JoinPoint point) {

    log.info("MessageQueueAopAspect2:doBefore");

    return;

  }

  

  //声明后置通知

  @AfterReturning(pointcut = "pointCutMethod()", returning = "returnValue")

  public void doAfterReturning(JoinPoint point,Object returnValue) {

    log.info("MessageQueueAopAspect2:doAfterReturning");

  }

  

  //声明例外通知

  @AfterThrowing(pointcut = "pointCutMethod()", throwing = "e")

  public void doAfterThrowing(Exception e) {

    log.info("MessageQueueAopAspect2:doAfterThrowing");

  }

  

  //声明最终通知

  @After("pointCutMethod()")

  public void doAfter() {

    log.info("MessageQueueAopAspect2:doAfter");

  }

  

  //声明环绕通知

  @Around("pointCutMethod()")

  public Object doAround(ProceedingJoinPoint pjp) throws Throwable {

    log.info("MessageQueueAopAspect2:doAround-1");

    Object obj = pjp.proceed();

    log.info("MessageQueueAopAspect2:doAround-2");

    return obj;

  }

    

  @Override

  public int getOrder() {

    return 1002;

  }

}

1

2

3

4

5

6

@Transactional(propagation=Propagation.REQUIRES_NEW)

@MessageQueueRequire1

@MessageQueueRequire2

public PnrPaymentErrCode bidLoan(String id){

       ...

    }

看看执行结果:

从上面的测试我们看到,确实是order越小越是最先执行,但更重要的是最先执行的最后结束。

这个不难理解,Spring AOP就是面向切面编程,什么是切面,画一个图来理解下:

由此得出:spring aop就是一个同心圆,要执行的方法为圆心,最外层的order最小。从最外层按照AOP1、AOP2的顺序依次执行doAround方法,doBefore方法。然后执行method方法,最后按照AOP2、AOP1的顺序依次执行doAfter、doAfterReturn方法。也就是说对多个AOP来说,先before的,一定后after。

如果我们要在同一个方法事务提交后执行自己的AOP,那么把事务的AOP order设置为2,自己的AOP order设置为1,然后在doAfterReturn里边处理自己的业务逻辑。

 

总结:

如果两个切面中覆写的getOrder方法中返回的是0,就是还没有显式的指定不同的顺序,所以,根据跟踪源码,可以发现在order相同的情况下, 是根据切面类的名称字母序进行优先级控制的,字母序越靠前,优先级越高。字母序的比较,首先将类名转换为字符串,然后调用StringcompareTo()方法,对两个类名进行比对,决定切面的排序的。如果切面类使用了@Order注解或者是实现了Ordered接口,那么可以在比对的时候自动调用getOrder()的方法,然后比较返回的值大小,值越小,优先级越高

同一个切面类中的方法,如果有多个不同的切入方式,例如@Around,@Before,@After,@AfterReturning,@AfterThrowing,那么会先扫描出各个方法上的注解,对不同的方法按照上边注解的顺序进行排序,然后按照字母序进行排序,所以最终呈现出来的,同一个切面类中的不同切面方法的执行顺序,就会呈现如上所示的状态。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

spring 多个切面的执行顺序及原理 的相关文章

随机推荐

  • 00.JavaScript基础

    0o 参考资料 js https codeofli github io 2019 11 js note javaScript javaScript vue https codeofli github io 2019 11 js note v
  • @ConfigurationProperties灵活的映射配置信息

    介绍 在用 ConfigurationProperties最常用的功能是用此注解对类进行修饰 设置好prefix前缀 这样在springboot的配置文件中 配置信息的key和value就会对应的配置到类中的属性上 以设置eureka信息为
  • nas挂载windows_【群晖系统】群晖下直接挂载WINDOWS的NTFS格式硬盘

    群晖的硬盘格式是EXT4 相对于WINDOWS下的NTFS格式 大家较不熟悉 在数据管理 使用 恢复等都不如NTFS方便 如果群晖能支持NTFS格式就好了 相信每一位装黑群晖的朋友当时都会有这样的想法 其实 群晖是支持外部设备的NTFS格式
  • c++实现引用计数

    概述 当有指针指向同一块内存空间时 计数器加1 没增加一个指向该内存空间的指针 计数器加1 同理 当原本指向该内存空间的指针指向另一块内存 计数器减1 被指向的另一个内存的计数器加1 下面是一个引用计数的一种实现 示例 直接上代码 总共分为
  • uni-app项目中如何使用scss less

    前言 由于公司业务调整 特意学习下uni项目框架 其实根据官方api就是实现很多功能 其实都是一些小坑要走 下面来说一下uni项目中如何使用scss vue编写中我们可以直接使用下面这样方法 多方便
  • Eclispse中Run on Server窗口让选择Server,但已经存在的选择不了

    对于这种问题 通常是因为版本不匹配造成的 jdk版本 Dynamic Web Modules版本 只要改到相应版本就好了 jdk7 时Dynamic Web Modules应设为2 5 如果无法修改 可以新建一个工程 在新建工程时选择Dyn
  • 记忆深处有尘埃——Memory Compiler

    Memory是大家Floorplan中经常使用到一个器件 而且需要花费不少时间去摆放它 Memory的种类很多 各种类型还分别具有不同的参数 那大家有没有想过 对一个设计来说 我们是如何去选择合适的memory类型 不同的类型有什么区别 在
  • 作为一名程序员,如何开展自己的副业?月赚三万的真实故事

    作为一名程序员 除了敲代码之外还应该有一些副业 我们都是程序员 大多数都是普通人 都在替别人打工 虽然收入在别人眼中挺高 但是连个首付都付不起 这时 首先得要发展副业 与其拿着死工资 还不如做些啥 今天 我所说的不是教大家如何去挣很多钱 而
  • mavon-editor 页面回显使用turndown将HTML转为markdown

    1 安装npm install turndown npm install turndown 2页面使用 v model markdowntext
  • 后端接口返回近万条数据,前端渲染缓慢,content Download 时间长的优化方案

    前言 性能优化 是前端绕过不去的一道门槛 甚是重要 最近一年 也很少有机会在项目中进行前端性能优化 一直在忙于业务开发 最近终于是来了机会 遇到了这样的场景 心里也甚是激动 写个随笔记录下性能优化的过程及逻辑 有需要的可以参考下 场景 后端
  • 机器学习实战笔记8(kmeans)

    前面的7次笔记介绍的都是分类问题 本次开始介绍聚类问题 分类和聚类的区别在于前者属于监督学习算法 已知样本的标签 后者属于无监督的学习 不知道样本的标签 下面我们来讲解最常用的kmeans算法 1 kmeans算法 算法过程 Kmeans中
  • Spring核心思想 IOC 、 AOP

    Spring核心思想 IOC AOP IOC 1 什么是IOC 2 IOC解决了什么问题 IoC解决对象之间的耦合问题 3 IOC和DI的区别 AOP 1 什么是AOP 2 AOP在解决什么问题 3 为什么叫切面编程 内容就不展示了 里面已
  • Python自动检查哪位学生未提交作业

    最近期未需要对学生提交的作业进行统计 给平时成绩 总共交了8次作业 每个作业都有2个班 数量太多 于是就利用Python写了一个程序来自动实现 思想 获取指定路径下的所有文件名 如果文件名中包含了学生的名字 因为提交作业的时候以学号 名字进
  • 基于TMF SID的高可扩展性数据模型

    基于TMF SID的高可扩展性数据模型 前言 此文根据TMF SID规范撰写 欢迎大家提出建议和意见 TMF文档版权信息 Copyright TeleManagement Forum 2013 All Rights Reserved Thi
  • Flutter Windows应用开发环境配置

    为什么要入Flutter开发的坑 首先在当今Windows开发已经逐渐成为一个偏小众的领域 不仅要涉及的知识面广 还对开发人员的要求不低 界面的精美也成为一个重要因素 目前已知的Windows 客户端主要分成以下几种 开发语言 Qt C C
  • Android登录 之 Twitter登录

    作为Android登录 之 GooglePlay登录的姊妹篇 这俩篇主要是对接国外平台登录的文章 作者文笔并不好 但是 管他呢 实现功能不就得了嘛 Twitter官网 兄弟们自带梯子啊 然后按照流程 创建申请什么的 也就不多说了 接下来就是
  • Google C++风格指南 阅读笔记

    这个Google C 风格指南出得太好了 有很多C 的问题 其实通过阅读这份文档就可以了 相信读完后 可以在简历上加上一句 具有良好的编码风格 哈哈 下面记录一下我的读书笔记吧 整份文档的中文版本我已经上传到了资源里面 1 头文件 1 1头
  • 在vue使用jsx来解决template中复杂的逻辑处理

    1 首先安装依赖 npm install postcss loader autoprefixer babel loader babel core 2 在 babelrc文件中修改 把 presets env stage 2 plugins
  • 【Python】Windows如何在cmd中切换python版本

    相信很多小伙伴都会有像我一样经历 在windows中装了很多python版本 那么如果我们正式使用的时候应该如何切换呢 方法一 从环境变量中切换python 第一步 打开环境变量 第二步 打开系统变量中Path变量 第三步 将你想使用的Py
  • spring 多个切面的执行顺序及原理

    最近和同事聊起来了springAOP的话题 说了到多个切面的时候程序是怎么执行的 我们常用的spring事务本身也是一个切面 使用的AOP原理 本人从网上找了一些资料 然后根据这些资料进行一下总结 资料地址 1 https blog csd