循环依赖底层源码解决方案

1、问题背景

什么是循环依赖?

循环依赖指的是两个或多个 Bean 互相依赖,如 BeanA 依赖 BeanB,BeanB 又依赖 BeanA。在传统的依赖注入过程中,这种相互依赖会导致死循环或栈溢出,Spring 需要一套机制来优雅解决此问题。

过程示例图:
在这里插入图片描述

如何解决循环依赖问题?

Spring 循环依赖的解决方案核心:三级缓存机制
Spring 使用三级缓存缓存正在创建的单例 Bean,利用**“提前曝光”**的方式断开循环依赖链,解决循环依赖的问题

2、三级缓存解决方案(DefaultSingletonBeanRegistry)

主要解决方案是在:package org.springframework.beans.factory.support.DefaultSingletonBeanRegistry类,其中getSingleton里面包含有三级缓存依赖的解决方案

2.1、三级缓存结构

在这里插入图片描述

变量名作用总结
singletonObjects一级缓存,完全初始化好的 Bean
earlySingletonObjects二级缓存,提前曝光的半成品 Bean
singletonFactories三级缓存,创建半成品 Bean 的工厂
registeredSingletons已注册单例 Bean 名称集合
singletonsCurrentlyInCreation正在创建中的 Bean 名称集合
inCreationCheckExclusions不参与创建检查的 Bean 名称集合
suppressedExceptions被抑制异常集合
singletonsCurrentlyInDestruction是否正在销毁单例 Bean
disposableBeans需要销毁的 Bean 实例集合
containedBeanMapBean 包含关系
dependentBeanMapBean 的依赖者关系
dependenciesForBeanMapBean 依赖关系

Spring 中的三级缓存主要用于单例 Bean 的生命周期管理,特别是在循环依赖时,它通过不同阶段暴露 Bean 实例来确保依赖注入的顺利完成。缓存的内容如下:

  1. 一级缓存 (singletonObjects)存储完全初始化的单例 Bean。该缓存中存储的是已经完成初始化的 Bean 对象,包括所有的属性注入和生命周期回调方法(如 @PostConstruct)都已完成。

  2. 二级缓存 (earlySingletonObjects)存储实例化但尚未完成所有初始化步骤的 Bean 引用。也就是 Bean 已经完成了实例化,并完成了依赖注入(如字段注入),但可能还没有执行初始化方法(如 @PostConstruct)。二级缓存用于解决循环依赖问题,当需要注入的 Bean 还没有完全初始化时,可以先返回该 Bean 的早期引用。

  3. 三级缓存 (singletonFactories)存储的是创建 Bean 的工厂(ObjectFactory),而不是直接存储 Bean 实例。这些工厂延迟返回真正的 Bean 实例,直到它们完成初始化。三级缓存用于处理 循环依赖 的核心,当一个 Bean 处于创建过程中,另一个 Bean 依赖它时,可以从三级缓存中获取一个工厂对象,该工厂会在 Bean 完全初始化后,生成一个完整的 Bean 实例。

2.2、三级缓存解决代码详解

@Nullable
    public Object getSingleton(String beanName) {
        return this.getSingleton(beanName, true);
    }


@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 先从一级缓存(完全初始化的Bean)中查找
    Object singletonObject = this.singletonObjects.get(beanName);

    // 如果一级缓存没找到,并且该Bean正处于创建中(避免重复创建)
    if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
        // 尝试从二级缓存(早期曝光的半成品)中查找
        singletonObject = this.earlySingletonObjects.get(beanName);

        // 如果二级缓存也没有,并且允许获取早期引用
        if (singletonObject == null && allowEarlyReference) {
            // 加锁,保证下面操作的线程安全
            synchronized(this.singletonObjects) {
                // 再次检查一级缓存(防止其他线程已初始化完成)
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    // 再次从二级缓存检查
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        // 从三级缓存获取ObjectFactory,创建早期半成品Bean
                        ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            // 通过工厂创建Bean的早期引用
                            singletonObject = singletonFactory.getObject();
                            // 放入二级缓存
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            // 从三级缓存移除工厂,避免重复创建
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }

    return singletonObject;
}

}

总结

singletonObject加锁的目的:
1、代码使用了经典的双重检查锁定模式

// 第一次检查(无锁)
if (singletonObject == null && allowEarlyReference) {
    synchronized(this.singletonObjects) {
        // 第二次检查(有锁)
        singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            // ...
        }
    }
}

2、 不加锁的核心问题
最关键的线程安全问题是:

// 无锁情况下,多个线程可能同时执行:
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
    singletonObject = singletonFactory.getObject(); // 可能被多次调用
    this.earlySingletonObjects.put(beanName, singletonObject);
    this.singletonFactories.remove(beanName);
}

这会导致同一个ObjectFactory被多次调用,创建多个实例,违反单例约束。

2.3、执行流程

  1. 一级缓存 (singletonObjects):
    检查一级缓存,获取完全初始化的 Bean。
    如果一级缓存中存在该对象,则直接返回。

  2. 二级缓存 (earlySingletonObjects):
    如果一级缓存中没有找到该对象,且该对象正在创建过程中(this.singletonsCurrentlyInCreation),则检查二级缓存。
    如果二级缓存中存在早期暴露的 Bean,则直接返回。

  3. 三级缓存 (singletonFactories):
    如果二级缓存中也没有,并且允许提前引用(allowEarlyReference 为 true),则从三级缓存中获取工厂对象(ObjectFactory)。
    调用 ObjectFactory.getObject() 方法生成 Bean 实例。
    将生成的实例放入二级缓存,同时从三级缓存中移除对应的工厂。

3、扩展

📍 定位核心类和方法
Spring 解决循环依赖的核心代码在:

  • 类:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
  • 关键方法:
    • getSingleton(String beanName):获取单例
    • addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory):添加早期工厂
    • addSingleton(String beanName, Object singletonObject):添加单例对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值