【老王读Spring AOP-3】Spring AOP 执行 Pointcut 对应的 Advice 的过程

前言

实现 Spring AOP 大体会分如下几步:

  1. 找到与 bean 匹配的所有的 Advisor
  2. 使用所有匹配的 Advisor 来为 bean 生成生成动态代理
  3. 通过动态代理类执行 Advice
  4. 将 Spring AOP 与 Spring IoC 进行结合

前面已经分析了第一步 和 第二步,接下来,我们分析一下 第三步:如何通过动态代理类执行 Advice?

版本约定

Spring 5.3.9 (通过 SpringBoot 2.5.3 间接引入的依赖)

正文

Spring 的动态代理类分为两种: jdk proxy 和 cglib proxy。
下面我们就分别来研究一下。
proxyFactory

ProxyFactory 是 Spring AOP 用于生成 AOP 代理类的核心类。
AopProxyFactory 是创建 AOP 代理类的工厂接口,它只有一个实现类 DefaultAopProxyFactory,它会根据规则来具体选用 JDK proxy 或者 CGLIB proxy。

jdk proxy 是如何执行 Pointcut 对应的 Advice 的?

JDK proxy 是通过 java.lang.reflect.InvocationHandler#invoke() 来进行方法拦截的。
相应的,我们看下 JdkDynamicAopProxy#invoke() 的源码:
JdkDynamicAopProxy

可以看出,JDK proxy 执行 advice 大致分为两步:

  1. 获取与 method 匹配的 Advice 链(AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice)
  2. 使用 ReflectiveMethodInvocation 通过反射来执行 Advice 链 和 joinpoint(目标方法)

获取 Advice 链的过程

// org.springframework.aop.framework.AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice()
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
    MethodCacheKey cacheKey = new MethodCacheKey(method);
    List<Object> cached = this.methodCache.get(cacheKey);
    if (cached == null) {
        cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                this, method, targetClass);
        this.methodCache.put(cacheKey, cached);
    }
    return cached;
}

DefaultAdvisorChainFactory

获取 Advice 链是一个相对耗时的过程,在 proxy 对象 第一次 invoke 执行时,AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice() 会去匹配 method 对应的 Advice 链。

获取 Advice 链的过程,就是将所有的 Advisor 进行遍历,看是否与 Pointcut 匹配,即通过 Pointcut 中的 ClassFilter 和 MethodMatcher 进行匹配过滤,将匹配到的 Advisor 组成一个 Advisor 链(interceptorList)。

第一次获取 Advice 链之后,就会将 Advice 链缓存起来,所以,jdk proxy 类在第一次执行时,是相对比较耗时的

cglib proxy 是如何执行 Pointcut 对应的 Advice 的?

cglib 是通过 Enhancer#create() 来生成代理类的,真正起拦截作用的是 Enhancer 中设置的 Callback 类。

创建 Enhancer

所以,我们首先来看下 Spring AOP 是如何创建 Enhancer 并设置 Callback 的,对应的源码是 CglibAopProxy#getProxy() :
CglibAopProxy

组装 Callback

CglibAopProxy#getCallbacks() 获取 Callback:
getCallbacks

可以看出,普通的 Advice 是通过 DynamicAdvisedInterceptor 来拦截执行的。

拦截 method,执行 Advice 链

method 被 DynamicAdvisedInterceptor 拦截的过程:
DynamicAdvisedInterceptor

通过源码,可以看出,cglib proxy 最终拦截 method 之后的处理同 jdk proxy 一样,都是分为两步:

  1. 获取与 method 匹配的 Advice 链(AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice)
  2. 使用 ReflectiveMethodInvocation 通过反射来执行 Advice 链 和 joinpoint(目标方法)

小结

  • JDK proxy
    通过 JdkDynamicAopProxy#getProxy() 来产生代理对象,最终目标方法的拦截是通过 JdkDynamicAopProxy#invoke() 来实现的。
  • CGLIB proxy
    通过 CglibAopProxy#getProxy() 来产生代理对象,最终目标方法的拦截主要是通过 DynamicAdvisedInterceptor#intercept() 来实现的。

不管是 JDK proxy 还是 CGLIB proxy,最终处理目标方法的拦截过程是相同,都会经过两步:

  1. 获取与 method 匹配的 Advice 链(AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice)
  2. 使用 ReflectiveMethodInvocation 通过反射来执行 Advice 链 和 joinpoint(目标方法)

获取与 method 匹配的 Advice 链是一个相对耗时的操作,所以 Spring AOP 将获取到的 Advice 链进行了缓存。


如果本文对你有所帮助,欢迎点赞收藏!

源码测试工程下载:
老王读Spring IoC源码分析&测试代码下载
老王读Spring AOP源码分析&测试代码下载

公众号后台回复:下载IoC 或者 下载AOP 可以免费下载源码测试工程…

阅读更多文章,请关注公众号: 老王学源码
gzh


系列博文:
【老王读Spring AOP-0】SpringAop引入&&AOP概念、术语介绍
【老王读Spring AOP-1】Pointcut如何匹配到 join point
【老王读Spring AOP-2】如何为 Pointcut 匹配的类生成动态代理类
【老王读Spring AOP-3】Spring AOP 执行 Pointcut 对应的 Advice 的过程
【老王读Spring AOP-4】Spring AOP 与Spring IoC 结合的过程 && ProxyFactory 解析
【老王读Spring AOP-5】@Transactional产生AOP代理的原理
【老王读Spring AOP-6】@Async产生AOP代理的原理
【Spring 源码阅读】Spring IoC、AOP 原理小总结

相关阅读:
【Spring源码三千问】Spring动态代理:什么时候使用的 cglib,什么时候使用的是 jdk proxy?
【Spring源码三千问】Advice、Advisor、Advised都是什么接口?
【Spring源码三千问】没有AspectJ,Spring中如何使用SpringAOP、@Transactional?
【Spring源码三千问】Spring AOP 中 AbstractAdvisorAutoProxyCreator、AbstractAdvisingBeanPostProcessor的区别
【Spring 源码三千问】同样是AOP代理bean,为什么@Async标记的bean循环依赖时会报错?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

老王学源码

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

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

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

打赏作者

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

抵扣说明:

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

余额充值