【老王读Spring AOP-2】如何为 Pointcut 匹配的类生成动态代理类?

前言

通过前面文章的分析,我们知道,实现 Spring AOP 大体会分如下几步:

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

上一篇我们分析了 Pointcut 是通过 AspectJExpressionPointcut 来处理 aspectj expression 的匹配的。
本文接着来分析一下,Spring AOP 是如何通过匹配的 Advisor 来给 bean 生成代理类的。

版本约定

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

正文

为了研究 Spring 是如何通过匹配的 Advisor 来给 bean 生成代理类的,我们需要先找到 proxy bean 是什么时候创建的,我们可以从 bean 的创建三步曲中着手,我们在 AbstractAutowireCapableBeanFactory#initializeBean() 方法处打上断点来观察 bean 的创建情况。

bean 的创建三步曲:
1. createBeanInstance : 创建 bean 的实例
2. populateBean : 填充 bean 的依赖
3. initializeBean : 初始化 bean
详细的 bean 的创建过程请戳: 【老王读Spring IoC-3】Spring bean 的创建过程

initializeBean() 会去调用 applyBeanPostProcessorsAfterInitialization(),断点可以打在这个方法上:

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
        throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}
  • 如何快速找到 Spring 在哪里产生的代理 bean?
    答:我们在 applyBeanPostProcessorsAfterInitialization() 方法处打一个条件断点,断点的条件为:result != current,这样的话,就能快速定位到是哪个 processor 生成了代理类!

什么时候创建 proxy bean

可以发现,proxy bean 是在执行 AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization() 时产生的:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

// AbstractAutoProxyCreator#wrapIfNecessary()
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    ......

    // Create proxy if we have advice.
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 创建代理 bean  
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

Spring 是在 bean 创建时的第三步 initializeBean 时,调用 AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization() 来产生代理 bean 的。

如何创建 proxy bean

createProxy

通过阅读上面的源码,我们可以得出:

  1. Spring 在创建每个 proxy bean 时,都会 new 一个 ProxyFactory 来进行处理
  2. Spring 在创建代理类之前会将 targetClass 暴露到 BeanDefinition 的 attribute 中
  3. proxyTargetClass 可以由用户指定,但不是完全受用户控制的。如果 proxyTargetClass=false 的话,Spring 会通过内部的 check 来校正 proxyTargetClass 的值

ProxyFactory 使用 cglib 还是 jdk proxy?

// DefaultAopProxyFactory#createAopProxy()
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (!NativeDetector.inNativeImage() &&
            (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
        Class<?> targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException("TargetSource cannot determine target class: " +
                    "Either an interface or a target is required for proxy creation.");
        }
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        // proxyTargetClass=true && targetClass 不是接口时,使用 cglib  
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

spring 使用的 cglib 代理 还是 jdk proxy?
使用 cglib 还是 jdk 代理,大体是由两个因素共同决定的:一是 targetClass 的类型(接口 or 实体类);二是 proxyTargetClass 标识的值(true or false)。
而 proxyTargetClass 的值是由用户和 spring 框架共同决定的。

引用官方的话说:

  1. Spring 默认使用的是 JDK proxy,如果 target object 实现了接口的话。
  2. Spring 会使用 CGLIB proxy,如果 target object 没有实现接口的话。

If the target object to be proxied implements at least one interface, a JDK dynamic proxy is used. All of the interfaces implemented by the target type are proxied.
If the target object does not implement any interfaces, a CGLIB proxy is created.
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-proxying

其实,在 spring 内部,针对到底使用 cglib 还是 jdk proxy 还有些细节处理,我们后续会详细解析。

怎么去读取 @Pointcut、@Around、@Before、@After、@AfterReturning、@AfterThrowing 等 Advice 标识

getAdvice

支持的注解:

protected enum AspectJAnnotationType {
    AtPointcut, AtAround, AtBefore, AtAfter, AtAfterReturning, AtAfterThrowing
}

protected static class AspectJAnnotation<A extends Annotation> {
    private static Map<Class<?>, AspectJAnnotationType> annotationTypeMap = new HashMap<>(8);
    static {
        annotationTypeMap.put(Pointcut.class, AspectJAnnotationType.AtPointcut);
        annotationTypeMap.put(Around.class, AspectJAnnotationType.AtAround);
        annotationTypeMap.put(Before.class, AspectJAnnotationType.AtBefore);
        annotationTypeMap.put(After.class, AspectJAnnotationType.AtAfter);
        annotationTypeMap.put(AfterReturning.class, AspectJAnnotationType.AtAfterReturning);
        annotationTypeMap.put(AfterThrowing.class, AspectJAnnotationType.AtAfterThrowing);
    }
    ......
}

小结

Spring 是在 bean 创建时的第三步 initializeBean 时,调用 AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization() 来产生代理 bean 的。
每个代理 bean 的生成都是通过一个新 new 出来的 ProxyFactory 来处理的。


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

源码测试工程下载:
老王读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循环依赖时会报错?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

老王学源码

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

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

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

打赏作者

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

抵扣说明:

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

余额充值