文章目录
前言
在 SpringBoot源码解读与原理分析(十六)SpringBoot的AOP支持 5.1.2 通知类型 中提到,SpringFramework支持5种基于AspectJ的通知类型:
- Before前置通知
- After后置通知
- AfterReturning返回通知
- AfterThrowing异常通知
- Around环绕通知
本节梳理下这5中通知类的底层执行逻辑。
9.6.5 AspetJ通知的底层实现
9.6.5.1 Before前置通知
Before前置通知会先执行前置通知,再执行目标方法。
代码清单1:MethodBeforeAdviceInterceptor.java
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
// 执行前置通知
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
// 执行目标方法
return mi.proceed();
}
}
9.6.5.2 After后置通知
After后置通知会在执行目标方法后,再finally中执行后置方法。
代码清单2:AspectJAfterAdvice.java
public class AspectJAfterAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable {
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
// 执行目标方法
return mi.proceed();
}
finally {
// 执行后置通知
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
}
9.6.5.3 AfterReturning返回通知
AfterReturning返回通知会在目标方法执行不出现任何异常,有返回值时触发,不需要设置try-catch结构。
代码清单3:AfterReturningAdviceInterceptor.java
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
// 执行目标方法
Object retVal = mi.proceed();
// 执行后置通知
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
}
9.6.5.4 AfterThrowing异常通知
AfterThrowing异常通知会在目标方法运行时抛出异常后触发,因此需要设置try-catch结构,在catch结构中进行相应的后置处理。
代码清单4:AspectJAfterThrowingAdvice.java
public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable {
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
// 执行目标方法
return mi.proceed();
}
catch (Throwable ex) {
// 目标方法抛出异常,执行后置处理
if (shouldInvokeOnThrowing(ex)) {
invokeAdviceMethod(getJoinPointMatch(), null, ex);
}
throw ex;
}
}
}
AfterReturning返回通知和AfterThrowing异常通知是互斥的。如果方法成功调用无异常,则会有返回值;如果方法抛出了异常,则不会有返回值。
9.6.5.5 Around环绕通知
通过构建ProceedingJoinPoint对象,直接以参数形式传入通知方法的方法参数中,反射执行通知方法。
代码清单5:AspectJAroundAdvice.java
public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable {
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
JoinPointMatch jpm = getJoinPointMatch(pmi);
return invokeAdviceMethod(pjp, jpm, null, null);
}
}
Around环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接获取目标对象以及要执行的方法,因此环绕通知可以任意在目标对象的方法调用前后扩展逻辑,甚至不调用目标对象的方法。
9.7 AOP通知的执行顺序对比
不同的SpringFramework版本中AOP通知的执行顺序是不一样的。SpringBoot 2.x底层基于SpringFramework 5.x,而SpringBoot 1.x底层基于SpringFramework 4.x,两者的AOP通知的执行顺序存在差别。
9.7.1 编写测试代码
代码清单6
// 目标方法类
@Service
public class DemoService {
public void test() {
System.out.println("DemoService.test run ......");
}
}
// 切面类
@Aspect
@Component
public class DemoServiceAspect {
@Before("execution(public * com.xiaowd.springboot.aop.test01.*.*(..))")
public void beforeTest() {
System.out.println("DemoServiceAspect.beforeTest run ......");
}
@After("execution(public * com.xiaowd.springboot.aop.test01.*.*(..))")
public void afterTest() {
System.out.println("DemoServiceAspect.afterTest run ......");
}
@AfterReturning("execution(public * com.xiaowd.springboot.aop.test01.*.*(..))")
public void afterReturningTest() {
System.out.println("DemoServiceAspect.afterReturningTest run ......");
}
@AfterThrowing("execution(public * com.xiaowd.springboot.aop.test01.*.*(..))")
public void afterThrowingTest() {
System.out.println("DemoServiceAspect.afterThrowingTest run ......");
}
@Around("execution(public * com.xiaowd.springboot.aop.test01.*.*(..))")
public Object aroundTest(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("DemoServiceAspect.aroundTest run ......before");
Object obj = joinPoint.proceed();
System.out.println("DemoServiceAspect.aroundTest run ......after");
return obj;
}
}
// 主启动类
@SpringBootApplication
@EnableAspectJAutoProxy
public class Test01App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Test01App.class, args);
context.getBean(DemoService.class).test();
}
}
9.7.2 SpringFramework 5.x的顺序
运行主启动类的main
方法,控制台输出结果如下:
DemoServiceAspect.aroundTest run ......before
DemoServiceAspect.beforeTest run ......
DemoService.test run ......
DemoServiceAspect.afterReturningTest run ......
DemoServiceAspect.afterTest run ......
DemoServiceAspect.aroundTest run ......after
由输出结果可以总结出SpringFramework 5.x中通知的执行顺序:
- 环绕通知中
joinPoint.proceed()
之前的逻辑; - 前置通知;
- 目标对象的目标方法;
- 返回通知;
- 后置通知;
- 环绕通知中
joinPoint.proceed()
之后的逻辑。
9.7.3 SpringFramework 4.x的顺序
将SpringBoot的版本调整为1.x,即将pom.xml中spring-boot-starter-parent的版本调整为1.5.10.RELEASE。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
</parent>
再次运行主启动类的main
方法,控制台输出结果如下:
DemoServiceAspect.aroundTest run ......before
DemoServiceAspect.beforeTest run ......
DemoService.test run ......
DemoServiceAspect.aroundTest run ......after
DemoServiceAspect.afterTest run ......
DemoServiceAspect.afterReturningTest run ......
由输出结果可以总结出SpringFramework 5.x中通知的执行顺序:
- 环绕通知中
joinPoint.proceed()
之前的逻辑; - 前置通知;
- 目标对象的目标方法;
- 环绕通知中
joinPoint.proceed()
之后的逻辑; - 后置通知;
- 返回通知。
总结:SpringFramework 5.x与4.x中AOP通知顺序的不同之处在于环绕通知可以覆盖的范围。SpringFramework 5.x中的环绕通知会包含其他所有通知,而SpringFramework 4.x中的环绕通知只会包含前置通知。
9.8 小结
第9章到此就梳理完毕了,本章的主题是:AOP模块的生命周期。回顾一下本章的梳理的内容:
(二十八)AOP模块的生命周期(一)AnnotationAwareAspectJAutoProxyCreator
(二十九)AOP模块的生命周期(二)代理对象的底层创建逻辑
(三十)AOP模块的生命周期(三)代理对象的底层执行逻辑
(三十一)AOP模块的生命周期(四)AspetJ通知的底层实现
更多内容请查阅分类专栏:SpringBoot源码解读与原理分析
第10章主要梳理:SpringBoot整合JDBC。主要内容包括:
- SpringBoot整合JDBC的核心自动装配内容;
- 声明式事务的生效原理;
- 声明式事务的控制原理;
- 声明式事务的事务传播行为原理。