Spring AOP中增强Advice的执行顺序
- Spring AOP中
Advice
分类 -
同一
Apsect
中不同类型Advice
执行顺序- 配置基础环境
- 实验结果
- 结论
-
不同
Aspect
中Advice
执行顺序-
实验一:
Aspect1
为高优先级,Aspect2
为低优先级- 实验结果
-
实验二:
Aspect1
为低优先级,Aspect2
为高优先级- 实验结果
- 结论
-
实验一:
- 参考资料:
本文主要验证Spring AOP中Advice
的执行顺序问题。(Spring版本: 5.3.23)
Spring AOP中Advice
分类
Spring AOP中Advice
可分为如下五类:
@Around
@Before
@AfterReturning
@AfterThrowing
@After
Advice
相关概念参考
同一Apsect
中不同类型Advice
执行顺序
配置基础环境
- 依赖版本
- Spring 版本为: 5.3.23
- Spring Boot 版本为: 2.6.12
- aspectjweaver 版本: 1.9.9.1
- 定义Spring Boot启动类
package sakura.springinaction;
@SpringBootApplication
@EnableAspectJAutoProxy
public class MySpringApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringApplication.class, args);
}
}
- 定义一个用于测试的Controller类
package sakura.springinaction.controller;
@Controller
@Slf4j
public class IndexController {
@GetMapping("/time")
@ResponseBody
public String time() {
LocalDateTime now = LocalDateTime.now();
String nowTime = now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
log.info("Current time: " + nowTime);
return nowTime;
}
}
- 定义一个声明式切面
Apsect1
@Slf4j
@Component
@Aspect
public class Aspect1 {
// 定义 Point Cut 切面
@Pointcut("execution(public * sakura.springinaction.controller.*.*(..))")
public void controllerLayer() {
}
// 定义Advice
@Before("controllerLayer()")
private void beforeAdvice2() {
log.info("Aspect_1 # @Before");
}
@After("controllerLayer() && @annotation(getMapping)")
private void afterAdvice1(GetMapping getMapping) {
log.info("Aspect_1 # @afterAdvice" + " path: " + Arrays.toString(getMapping.value()));
}
@AfterReturning(pointcut = "controllerLayer()", returning = "val")
private void afterReturningAdvice(Object val) {
log.info("Aspect_1 # @AfterReturning" + " returnValue: " + val);
}
@AfterThrowing(pointcut = "controllerLayer()", throwing = "thrower")
private void afterThrowingAdvice(Throwable thrower) {
log.info("Aspect_1 # @AfterThrowing" + " thrower: " + thrower.getClass().getName());
}
@Around("controllerLayer() && @annotation(getMapping)")
private Object aroundAdvice(ProceedingJoinPoint pjp, GetMapping getMapping) throws Throwable {
// Around 前置处理
Stopwatch stopwatch = Stopwatch.createStarted();
log.info("Aspect_1 # @Around-Before" + " methodName: " + pjp.getSignature().getName() + ", path: " + Arrays.toString(getMapping.value()));
Object result = pjp.proceed();
// Around 后置处理
log.info("Aspect_1 # @Around-After" + " methodName: " + pjp.getSignature().getName() + ", runTime: " + stopwatch.elapsed(TimeUnit.NANOSECONDS));
return result;
}
}
实验结果
在 发起请求(http://localhost:8080/time)
后,日志输出如图:
结论
在同一个切面(Apsect
)定义中对于同一个Join Point
而言,不同类型的Advice
执行先后顺序依次是:
-
@Around
前置处理 @Before
-
@AfterReturning
/@AfterThrowing
@After
-
@Around
后置置处理
优先级说明:
- 对于进入
Join Point
的Advice
而言(比如:@Around
前置处理,@Before
),优先级越高,越先执行;- 对于从
Join Point
出来的Advice
而言(比如:@Around
后置处理,@After
),优先级越高,越后执行;- 优先级从高到低依次为:
@Around
,@Before
,@After
,@AfterReturning
,@AfterThrowing
;
PS:
如果在同一个切面(
Apsect
)中定义了两个同类型的Advice
(比如定义两个@Before
), 对于某个Join Point
而言这两个Advice
都匹配,那么这两个Advice
执行的先后顺序是无法确定的。
不同Aspect
中Advice
执行顺序
问: 当不同的Aspect
中的Advice
都匹配到了同一个Join Point
,那么那个Aspect
中的Advice
先执行,那个后执行呢?
答: 不确定 ,但是可以通过在class上添加注解@Order
指定优先级确定执行顺序(参考文档)
实验一: Aspect1
为高优先级,Aspect2
为低优先级
- 与
Aspect1
类似,再定义一个切面类Aspect2
,如下
package sakura.springinaction.advice;
import org.springframework.core.annotation.Order;
@Slf4j
@Component
@Aspect
@Order(2)
public class Aspect2 {
// 定义Advice
@Before("sakura.springinaction.advice.Aspect1.controllerLayer()")
private void beforeAdvice2() {
log.info("Aspect_2 # @Before");
}
@After("sakura.springinaction.advice.Aspect1.controllerLayer() && @annotation(getMapping)")
private void afterAdvice1(GetMapping getMapping) {
log.info("Aspect_2 # @afterAdvice" + " path: " + Arrays.toString(getMapping.value()));
}
@AfterReturning(pointcut = "sakura.springinaction.advice.Aspect1.controllerLayer()", returning = "val")
private void afterReturningAdvice(Object val) {
log.info("Aspect_2 # @AfterReturning" + " returnValue: " + val);
}
@AfterThrowing(pointcut = "sakura.springinaction.advice.Aspect1.controllerLayer()", throwing = "thrower")
private void afterThrowingAdvice(Throwable thrower) {
log.info("Aspect_2 # @AfterThrowing" + " thrower: " + thrower.getClass().getName());
}
@Around("sakura.springinaction.advice.Aspect1.controllerLayer() && @annotation(getMapping)")
private Object aroundAdvice(ProceedingJoinPoint pjp, GetMapping getMapping) throws Throwable {
Stopwatch stopwatch = Stopwatch.createStarted();
log.info("Aspect_2 # @Around-Before" + " methodName: " + pjp.getSignature().getName() + ", path: " + Arrays.toString(getMapping.value()));
Object result = pjp.proceed();
log.info("Aspect_2 # @Around-After" + " methodName: " + pjp.getSignature().getName() + ", runTime: " + stopwatch.elapsed(TimeUnit.NANOSECONDS));
return result;
}
}
-
Aspect1
添加@Order
注解指定优先级,如下
@Slf4j
@Component
@Aspect
@Order(1)
public class Aspect1 {
//...
}
此时,Aspect1
的优先级比Aspect2
的优先级高。
实验结果
实验结果如下:
说明:
高优先级的
Aspect1
中的@Around
前置处理和@Before
先于低优先级的Aspect2
执行,而@AfterReturning
,@After
和@Around
后置处理,则低优先级的Aspect2
先执行。
实验二: Aspect1
为低优先级,Aspect2
为高优先级
- 更改两个
Aspect
中@Order
注解优先级,如下:
@Slf4j
@Component
@Aspect
@Order(2)
public class Aspect1 {
//...
}
@Slf4j
@Component
@Aspect
@Order(1)
public class Aspect2 {
//...
}
实验结果
实验结果如下:
结论
- 当不同的
Aspect
中的Advice
都匹配到了同一个Join Point
,不同Aspect
中的Advice
执行顺序不确定。 - 通过在
Aspect
类上添加注解@Order
指定优先级,确定执行顺序,执行顺序满足如下规律- 对于
@Around
前置处理 和@Before
两种Advice
而言,所在的Aspect
优先级越高,越先执行 - 对于
@AfterReturning
,@AfterThrowing
,@After
和@Around
后置处理 类型的Advice
而言,所在的Aspect
优先级越高,越后执行
- 对于
参考资料:
- Aspect Oriented Programming with Spring
- AspectJ Programming Guide
本文主要目的是记录学习过程,加深对知识点理解; 如有行文有误,望指正。