Spring 单例bean循环依赖的解决

本文深入探讨Spring框架如何解决Bean之间的循环依赖问题,通过分析BeanA和BeanB的依赖关系,详细解释了Spring如何利用singletonObjects、earlySingletonObjects和singletonFactories等数据结构,确保即使在循环依赖的情况下也能正确创建和初始化Bean。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring bean循环依赖例子代码

Bean组件A : 依赖于Bean组件B

@Component
public class BeanA {
    BeanB b;

    @Autowired
    public void setB(BeanB b) {
        this.b = b;
    }

    public String describeB() {
        return " ==引用==> BeanB 实例 :" + b;
    }
}

Bean组件B : 依赖于Bean组件A

@Component
public class BeanB {
    BeanA a;

    @Autowired
    public void setA(BeanA a) {
        this.a = a;
    }


    public String describeA() {
        return " ==引用==> BeanA 实例 :" + a ;
    }
}

上面的循环依赖可以通过下图表示 :

属性依赖
属性依赖
beanA
beanB

上面的例子表达的是两个bean之间直接循环依赖,另外还会有两个bean之间间接循环依赖的情况,Spring对这两种循环依赖的处理并无不同,所以这里不再单独对间接循环依赖举例。

Springbean循环依赖的解决

接下来我们结合Spring对单例bean统一的获取过程和有关的数据结构分析Spring 对单例bean循环依赖的解决。

Spring对单例bean统一的获取过程

Spring对单例bean的统一的获取过程为 :

// 类 AbstractBeanFactory
#getBean
=> #doGetBean

而方法#doGetBean主要流程如下 :

  1. getSingleton(beanName,true)

    1. 如果singletonObjects包含beanName对应单例bean实例则返回之;
    2. 如果earlySingletonObjects包含beanName对应单例bean实例则返回之;
    3. 如果singletonFactories包含beanName对应单例bean实例工厂则:
      1. 从对应单例bean实例工厂获取单例bean实例;
      2. 将所获取的单例bean实例放到earlySingletonObjects;
      3. singletonFactories删除对应的单例bean实例工厂;

    如果该步骤能获取到单例实例,则直接返回不再继续;

  2. getSingleton(String beanName, () -> {createBean(...)})

    1. 标记单例实例处于创建过程中
    2. createBean
      1. 应用InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
      2. doCreateBean
        1. createBeanInstance 创建对象
        2. 应用MergedBeanDefinitionPostProcessors
        3. addSingletonFactory(beanName, () -> getEarlyBeanReference(...))
        4. populateBean 填充属性,依赖注入

          这里会递归触发所依赖的bean的创建

        5. initializeBean 初始化
    3. 标记单例实例创建结束
    4. 将新建单例实例正式登记到单例注册表
      1. 将所创建的单例bean实例放到singletonObjects;
      2. earlySingletonObjects删除对应的单例bean实例工厂;
      3. singletonFactories删除对应的单例bean实例工厂;

Spring辅助单例bean获取的数据结构

Spring使用了以下数据结构来保存处于不同创建阶段的单例bean实例 :

  • Map<String, Object> singletonObjects 缓存已经创建完成的单例bean对象
    • 实例已经创建,并且已经完成属性设置,依赖注入和初始化
  • Map<String, Object> earlySingletonObjects 缓存尽早暴露的单例bean对象
    • 实例已经创建,还没有完成属性设置,依赖注入和初始化
  • Map<String, ObjectFactory<?>> singletonFactories 缓存单例bean名称和对应的ObjectFactory对象
  • Set<String> singletonsCurrentlyInCreation 当前正在创建过程中的单例bean的名称
    • 正在创建过程中的bean实例

分析组件BeanA/BeanB循环依赖的解决

为了方便描述,这里我们将BeanA单例实例的获取过程标记为#doGetBeanA,将BeanB单例实例的获取过程标记为#doGetBeanB。然后我们从#doGetBeanA的执行开始,结合以上辅助数据结构分析Spring对组件BeanA/BeanB循环依赖的解决。

#doGetBeanA开始之前

数据结构对组件BeanA/BeanB的信息记录
singletonObjects[]
earlySingletonObjects[]
singletonFactories[]

#doGetBeanA执行步骤2.2.3结束时

数据结构对组件BeanA/BeanB的信息记录
singletonObjects[]
earlySingletonObjects[]
singletonFactories[beanA的ObjectFactory]

#doGetBeanA执行到步骤2.2.4触发所依赖组件BeanB的获取

这一步骤会触发所依赖组件BeanB的获取,也就是我们上面我们所定义的过程#doGetBeanB的执行,#doGetBeanB开始之前以下数据结构状态保持跟上面相同 :

数据结构对组件BeanA/BeanB的信息记录
singletonObjects[]
earlySingletonObjects[]
singletonFactories[beanA的ObjectFactory]

#doGetBeanB执行步骤2.2.3结束时

数据结构对组件BeanA/BeanB的信息记录
singletonObjects[]
earlySingletonObjects[]
singletonFactories[beanA的ObjectFactorybeanB的ObjectFactory]

#doGetBeanB执行步骤2.2.4触发所依赖组件BeanA的获取

这一步骤会触发所依赖组件BeanA的获取,也就是我们上面我们所定义的过程#doGetBeanA的第二次执行,#doGetBeanA再次开始之前各数据结构保持跟上面一样 :

数据结构对组件BeanA/BeanB的信息记录
singletonObjects[]
earlySingletonObjects[]
singletonFactories[beanA的ObjectFactorybeanB的ObjectFactory]

#doGetBeanA二次执行步骤1.3结束时

因为#doGetBeanA第一次执行已经在步骤2.2.3添加了组件BeanAsingletonFactorysingletonFactories,所以#doGetBeanA二次执行时会检测到该信息从而使用它直接创建组件BeanA的及早暴露实例并返回。

这里之所以将这里创建的组件BeanA实例称作及早暴露实例,是因为整个#doGetBeanA过程尚未执行完成就需要将其实例暴露给使用者。

此时各个数据结构的内容如下 :

数据结构对组件BeanA/BeanB的信息记录
singletonObjects[]
earlySingletonObjects[beanA]
singletonFactories[beanB的ObjectFactory]

一旦获取到组件BeanA单例实例,#doGetBeanB将可以继续执行直到#doGetBeanB执行到2.4步骤结束。

#doGetBeanB执行到步骤2.4结束时

各个数据结构的内容如下 :

数据结构对组件BeanA/BeanB的信息记录
singletonObjects[beanB]
earlySingletonObjects[beanA]
singletonFactories[]

#doGetBeanB执行到步骤2.4结束时,也就意味着组件BeanB的创建结束了,#doGetBeanA的首次执行可以继续进行了,因为它现在拿到了所需要的依赖BeanB的单例实例。接下来,它会一直执行直到它的步骤2.4结束时。

#doGetBeanA执行到步骤2.4结束时

各个数据结构的内容如下 :

数据结构对组件BeanA/BeanB的信息记录
singletonObjects[beanA,beanB]
earlySingletonObjects[]
singletonFactories[]

到此为止,存在循环依赖的组件BeanA/BeanB的单例实例获取就已经圆满完成了,相应的循环依赖属性也得到了正确的处理。

参考文章

Spring - Circular Dependencies

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值