聊聊 Spring AOP 的不为常知的“秘事”
Spring AOP 在我们日常开发中扮演了一个非常重要的角色,对于如何使用 AOP 相信很多人已经不陌生,但其中有一些点却容易被我们忽视,本节我们结合一些“不为常知”的问题展开讨论。
同一个 AOP 类中几个切面注解的执行顺序
先给出结论:@Around [joinPoint.proceed()前] —> @Before —> @Around [joinPoint.proceed()以及之后] —> @After —> @AfterReturning(如果有异常则@AfterThrowing) 。
话不多说,我这们直接上代码:
@Aspect
public class AopClass {
@Pointcut(value = "execution(* io.alan.*.Klass.*dong(..))")
public void point() {
}
@Before(value = "point()")
public void before() {
System.out.println("========>begin klass dong... //2");
}
@Around("point()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("========>around begin klass dong //1");
joinPoint.proceed();
System.out.println("========>around after klass dong //3");
}
@After(value = "point()")
public void after() {
System.out.println("========>after klass dong... //4");
}
@AfterReturning(value = "point()")
public void afterReturning() {
System.out.println("========>after klass dong... //5");
}
}
查看应用打印的日志,如下:

不同 AOP 类切面的执行顺序
如果我们对同一个方法定义多个 AOP,它的执行顺序是什么样的呢?
配置 AOP 执行顺序的三种方式
方式1:实现 org.springframework.core.Ordered 接口
@Aspect
public class AopClass implements Ordered {
@Override
public int getOrder() {
return 2;
}
}
方式2:使用注解 @Order
@Aspect
@Order(2)
public class AopClass {
}
方式3:使用配置文件
<aop:config expose-proxy="true">
<aop:aspect ref="aopBean" order="2">
<aop:pointcut id="testPointcut" expression="@annotation(xxx.xxx.xxx.annotation.xxx)"/>
<aop:around pointcut-ref="testPointcut" method="doAround" />
</aop:aspect>
</aop:config>
代码测试
下面我们演示在同一份代码上加上两个 AOP,然后观察日志。
@Aspect
@Order(2)
public class Aop2 {
@Pointcut(value = "execution(* io.kimmking.*.Klass.*dong(..))")
public void point() {
}
@Before(value = "point()")
public void before() {
System.out.println(" Aop2========>before klass dong... //2");
}
@AfterReturning(value = "point()")
public void afterReturning() {
System.out.println(" Aop2========>afterReturning klass dong... //5");
}
@After(value = "point()")
public void after() {
System.out.println(" Aop2========>after klass dong... //4");
}
@Around("point()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println(" Aop2========>around before klass dong //1");
joinPoint.proceed();
System.out.println(" Aop2========>around after klass dong //3");
}
}
@Aspect
@Order(3)
public class Aop3 {
@Pointcut(value = "execution(* io.kimmking.*.Klass.*dong(..))")
public void point() {
}
@Before(value = "point()")
public void before() {
System.out.println(" Aop3========>before klass dong... //2");
}
@AfterReturning(value = "point()")
public void afterReturning() {
System.out.println(" Aop3========>afterReturning klass dong... //5");
}
@After(value = "point()")
public void after() {
System.out.println(" Aop3========>after klass dong... //4");
}
@Around("point()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println(" Aop3========>around before klass dong //1");
joinPoint.proceed();
System.out.println(" Aop3========>around after klass dong //3");
}
}
打印的日志如下:

从日志中我们可以看到,order 越小的 AOP 类越先执行,但还有一点需要我们注意:就是最先执行的 AOP 最后才执行结束(如上图中的 AOP2)。
我们通过下面的这张示意图就可以理解了。

小结一下:我们可以将 Spring AOP 当作一个同心圆,要执行的方法为圆心,最外层的 order 最小。从最外层按照 AOP1、AOP2 的顺序依次执行 Around 方法,Before 方法。然后执行 method 方法,最后按照 AOP2、AOP1 (顺序反过来了)的顺序依次执行 After、AfterReturn 方法。
所以对多个AOP来说,先执行 before 的,一定后执行 after。
例如,如果我们要在同一个方法事务提交后执行自己的 AOP ,那么可以把事务的 AOP order 设置为2,自己的 AOP order 设置为1,然后在 AfterReturn 里边处理自己的业务逻辑。
参考链接
- https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core
- https://blog.csdn.net/rainbow702/article/details/52185827
聊聊 Spring AOP 的不为常知的“秘事”的更多相关文章
- Spring -- aop(面向切面编程),前置&后置&环绕&抛异常通知,引入通知,自动代理
1.概要 aop:面向方面编程.不改变源代码,还为类增加新的功能.(代理) 切面:实现的交叉功能. 通知:切面的实际实现(通知要做什么,怎么做). 连接点:应用程序执行过程期间,可以插入切面的地点. ...
- 从源码层面聊聊面试问烂了的 Spring AOP与SpringMVC
Spring AOP ,SpringMVC ,这两个应该是国内面试必问题,网上有很多答案,其实背背就可以.但今天笔者带大家一起深入浅出源码,看看他的原理.以期让印象更加深刻,面试的时候游刃有余. Sp ...
- Spring AOP常见面试题
一.AOP是什么? 与OOP对比,面向切面,传统的OOP开发中的代码逻辑是至上而下的过程中会长生一些横切性问题,这些横切性的问题和我们的主业务逻辑关系不会散落在代码的各个地方,造成难以维护,AOP的编 ...
- 死磕Spring之AOP篇 - Spring AOP常见面试题
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...
- 学习Spring5必知必会(5)~Spring AOP
一.学习 AOP 思想的准备工作: 1.横切面关注点 在开发中,为了给业务方法中增加日志记录,权限检查,事务控制等功能,此时我们需要在修改业务方法内添加这些零散的功能代码(横切面关注点). 这些零散存 ...
- 比较 Spring AOP 与 AspectJ
本文翻译自博客Comparing Spring AOP and AspectJ(转载:https://juejin.im/post/5a695b3cf265da3e47449471) 介绍 如今有多个 ...
- 关于 Spring AOP (AspectJ) 该知晓的一切
关联文章: 关于Spring IOC (DI-依赖注入)你需要知道的一切 关于 Spring AOP (AspectJ) 你该知晓的一切 本篇是年后第一篇博文,由于博主用了不少时间在构思这篇博文,加上 ...
- 关于 Spring AOP (AspectJ) 你该知晓的一切
版权声明:本文为CSDN博主「zejian_」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/javazej ...
- Spring AOP源码分析--代理方式的选择
能坚持别人不能坚持的,才能拥有别人未曾拥有的.关注编程大道公众号,让我们一同坚持心中所想,一起成长!! 年前写了一个面试突击系列的文章,目前只有redis相关的.在这个系列里,我整理了一些面试题与大家 ...
随机推荐
- MindSpore数据集mindspore::dataset
MindSpore数据集mindspore::dataset ResizeBilinear #include <image_process.h> bool ResizeBilinear(L ...
- 视频系列:RTX实时射线追踪(下)
视频系列:RTX实时射线追踪(下) Key things from part 4 光线有效载荷是从一个着色器传递到另一个着色器的结构. 这一切都发生在RTX的引擎下. 更小的有效载荷要好得多! 新的D ...
- C语言代码区错误以及编译过程
C语言代码区错误 欲想了解C语言代码段会有如何错误,我们必须首先了解编译器是如何把C语言文本信息编译成为可以执行的机器码的. 背景介绍 测试使用的C语言代码 导入标准库,定义宏变量,定义结构体,重命名 ...
- UiPath 中 List 集合的实例化与使用
>>>跳过BB,空降正文<<< 目录 前言 正文 1. 创建 List 变量 2. 实例化 List 变量 3. 集合的使用 后记 前言 大家好呀,我是 白墨,一个 ...
- python+requests接口用例
本实例通过请求接口登录系统,获取了配置项的ID,并最终实现了对配置项的默认值进行修改 使用到的接口请求方法有:get(查询) ,post(新增),put(修改) 遇到的阻碍点见下面具体代码处的详解 编 ...
- NoSQL:如何使用NoSQL架构构建实时广告系统
JDNoSQL平台是什么 JDNoSQL平台是一个分布式面向列的KeyValue毫秒级存储服务,存储结构化数据和非机构化数据,支持随机读写与更新,灵活的动态列机制,架构上支持水平扩容,提供高并发.低延 ...
- Linkerd 2.10(Step by Step)—将 GitOps 与 Linkerd 和 Argo CD 结合使用
Linkerd 2.10 系列 快速上手 Linkerd v2.10 Service Mesh(服务网格) 腾讯云 K8S 集群实战 Service Mesh-Linkerd2 & Traef ...
- Windows操作系统添加永久静态路由
1.比如:添加一条去往 10.10.10.0/24网段的静态路由,指定去往此网段的路由都走 172.20.153.254网关 route -p add 10.10.10.0 mask 255.255. ...
- 『心善渊』Selenium3.0基础 — 5、XPath路径表达式详细介绍
目录 1.XPath介绍 2.什么是XML 3.XML与HTML对比 4.为什么使用XPath定位页面中的元素 5.XPath中节点之间的关系 (1)节点的概念 (2)节点之间的关系类型 6.XPat ...
- 轻松吃透实时时钟芯片DS1302软硬件设计,看完秒懂
今天我们来讨论一款老掉牙的实时时钟芯片DS1302.什么是实时时钟(RealTime Clock, RTC)呢?为什么我们需要它呢?假设你使用单片机实现万年历应用,一般的做法是这样的:设置中断后判断1 ...