【Spring 源码三千问】同样是AOP代理bean,为什么@Async标记的bean循环依赖时会报错?SmartInstantiationAwareBeanPostProcessor解析

前言

前面在分析 哪些循环依赖问题Spring解决不了 时,我们讲过,AOP 代理 bean 在被循环依赖时分两种情况:

  1. 普通的 AOP 代理 bean 被循环依赖,Spring 是支持的
  2. @Async 产生的 AOP 代理 bean 被循环依赖时,Spring 是不支持的,需要通过添加 @Lazy 来解决

那么,为什么同样是 AOP 代理 bean,差别就这么大呢?

版本约定

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

正文

先回顾一下 Spring 是如何解决循环依赖的: Spring 是通过三级缓存来解决循环依赖的问题的。

Spring 解决循环依赖核心原理是:
当 beanX 被循环依赖时,这时一级缓存中还没有 beanX,就会通过 beanX 对应的三级缓存 Map<String, ObjectFactory<?>> singletonFactories 来获取 bean 的早期引用。

获取 bean 的早期引用的代码实现如下:

// AbstractAutowireCapableBeanFactory#getEarlyBeanReference()  
/**
 * Obtain a reference for early access to the specified bean, typically for the purpose of resolving a circular reference.
 * 获取指定 bean 的早期引用,通常用于解析循环引用。
 */
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
            exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
        }
    }
    return exposedObject;
}

可以看出,通过三级缓存 Map<String, ObjectFactory<?>> singletonFactories 来获取 bean 的早期引用时,如果当前容器中有 SmartInstantiationAwareBeanPostProcessor,那么就通过 SmartInstantiationAwareBeanPostProcessor 来获取 bean 的早期引用。

想深入了解 Spring 是怎样解决循环依赖的?三级缓存的作用是什么?,请戳: 【Spring源码三千问】Spring 是怎样解决循环依赖问题的?

SmartInstantiationAwareBeanPostProcessor

SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference 只被 AbstractAutoProxyCreator#getEarlyBeanReference 实现了。
代码如下:

// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference
public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    // 是否需要创建代理类  
    return wrapIfNecessary(bean, beanName, cacheKey);
}

可以看出它会调用 AbstractAutoProxyCreator#wrapIfNecessary(),也就是 Spring 创建 AOP 代理类时调用的方法。
也就是说,getEarlyBeanReference() 会提前调用 AbstractAutoProxyCreator#wrapIfNecessary() 来生成 AOP 代理类。

这样的话,当普通的 AOP 代理 bean 被循环依赖时,就能被正确的注入属性引用。

普通的 AOP 代理 bean: 指通过 AbstractAutoProxyCreator#wrapIfNecessary() 产生 AOP 代理的 bean。
其中包括:
1. 自定义的 @Aspect 产生 AOP 代理的 bean
2. @Transactional 产生的 AOP 代理 bean

非 SmartInstantiationAwareBeanPostProcessor 生产代理的情况

Spring 产生 AOP 代理是在 bean 创建的第三步 initializeBean 的时候,通过 BeanPostProcessor#postProcessAfterInitialization 来产生 AOP 代理类的。
在 Spring 中其实是有两种方式产生 AOP 代理的:

  1. 通过 AbstractAdvisorAutoProxyCreator --> 即: AbstractAutoProxyCreator#wrapIfNecessary()
  2. 通过 AbstractAdvisingBeanPostProcessor

也就是说,Spring 除了使用 AbstractAutoProxyCreator 来产生 AOP 代理 bean 之外,还可以通过 AbstractAdvisingBeanPostProcessor 来产生 AOP 代理 bean。
而 AbstractAdvisingBeanPostProcessor 是没有实现 SmartInstantiationAwareBeanPostProcessor 的。

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
		implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
    ......
}

public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {
    .......
}

通过 AbstractAdvisingBeanPostProcessor 产生的 AOP 代理 bean 被循环依赖时,通过三级缓存 Map<String, ObjectFactory<?>> singletonFactories 来获取 bean 的早期引用时,就不会提前创建 AOP 代理 bean,也就是拿不到最终暴露到 Spring 容器中的 AOP 代理 bean 的早期引用,这样就会导致这种 AOP 代理 bean 循环依赖注入时的引用不正确。
这种情况是不被允许的,Spring 在 initializeBean 之后,做了 check,检验二级缓存中的 bean 与最终暴露到 Spring 容器中的 bean 是否是相同的,如果不同,就会报错。

proxyCircleRefer

而 @Async、@Valid 都是使用 AbstractAdvisingBeanPostProcessor 来产生 AOP 代理的。
综上,@Async、@Valid 产生的 AOP 代理 bean 被循环依赖时,会导致 Spring 容器启动异常,是不支持这种循环依赖的。

关于 AbstractAdvisorAutoProxyCreator 与 AbstractAdvisingBeanPostProcessor 可以戳: 【Spring源码三千问】Spring AOP 中 AbstractAdvisorAutoProxyCreator、AbstractAdvisingBeanPostProcessor的区别

小结

在 Spring 中有两种方式产生 AOP 代理:

  1. 通过 AbstractAdvisorAutoProxyCreator,即: AbstractAutoProxyCreator#wrapIfNecessary()
    被用户自定义的 @Aspect 拦截产生的 AOP 代理 bean,都是走这种方式
  2. 通过 AbstractAdvisingBeanPostProcessor
    @Async、@Valid 产生的 AOP 代理 bean 是走这种方式

Spring 支持普通的 AOP 代理 bean 被循环依赖;
Spring 不支持 @Async、@Valid 产生的 AOP 代理 bean 被循环依赖,需要通过添加 @Lazy 来解决


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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

老王学源码

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

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

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

打赏作者

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

抵扣说明:

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

余额充值