SpringBoot源码解读与原理分析(三十一)AOP模块的生命周期(四)AspetJ通知的底层实现

前言

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前置通知会先执行前置通知,再执行目标方法。

代码清单1MethodBeforeAdviceInterceptor.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中执行后置方法。

代码清单2AspectJAfterAdvice.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结构。

代码清单3AfterReturningAdviceInterceptor.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结构中进行相应的后置处理。

代码清单4AspectJAfterThrowingAdvice.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对象,直接以参数形式传入通知方法的方法参数中,反射执行通知方法。

代码清单5AspectJAroundAdvice.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中通知的执行顺序:

  1. 环绕通知中joinPoint.proceed()之前的逻辑;
  2. 前置通知;
  3. 目标对象的目标方法;
  4. 返回通知;
  5. 后置通知;
  6. 环绕通知中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中通知的执行顺序:

  1. 环绕通知中joinPoint.proceed()之前的逻辑;
  2. 前置通知;
  3. 目标对象的目标方法;
  4. 环绕通知中joinPoint.proceed()之后的逻辑;
  5. 后置通知;
  6. 返回通知。

总结: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的核心自动装配内容;
  • 声明式事务的生效原理;
  • 声明式事务的控制原理;
  • 声明式事务的事务传播行为原理。
  • 28
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灰色孤星A

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值