Spring 三级缓存机制详解(解决循环依赖)

        Spring 通过 三级缓存 解决单例 Bean 的循环依赖问题,核心思想是 提前暴露未完全初始化的 Bean 引用。以下是三级缓存的工作原理和完整处理流程:

1. 三级缓存定义

        请结合Spring Bean 的生命周期详解理解。

缓存名称存储内容作用阶段
singletonFactories存放 Bean 工厂对象ObjectFactory),用于生成未完全初始化的 BeanBean 实例化后,属性注入前
earlySingletonObjects存放 提前暴露的 Bean 实例(已实例化但未完成属性注入)属性注入阶段,解决循环依赖时使用
singletonObjects存放 完全初始化后的单例 Bean(已完成属性注入和初始化)Bean 完全初始化后

2. 循环依赖处理流程 

 以 ServiceA 依赖 ServiceB,同时 ServiceB 依赖 ServiceA 为例:

步骤 1:实例化 ServiceA
// 1. 反射创建 ServiceA 实例(此时未注入属性)
ServiceA serviceA = new ServiceA();

// 2. 将 ServiceA 的 ObjectFactory 存入 singletonFactories
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, bean));
  • singletonFactories 内容

    {
      "serviceA": ObjectFactory<ServiceA>
    }
步骤 2:ServiceA 属性注入(发现依赖 ServiceB
// 1. 发现需要注入 ServiceB
if (dependency instanceof ServiceB) {
    // 2. 尝试从容器获取 ServiceB(触发 ServiceB 的创建)
    ServiceB serviceB = getBean("serviceB");
}
步骤 3:实例化 ServiceB
// 1. 反射创建 ServiceB 实例
ServiceB serviceB = new ServiceB();

// 2. 将 ServiceB 的 ObjectFactory 存入 singletonFactories
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, bean));
  • singletonFactories 内容
    {
      "serviceA": ObjectFactory<ServiceA>,
      "serviceB": ObjectFactory<ServiceB>
    }
 步骤 4:ServiceB 属性注入(发现依赖 ServiceA
// 1. 发现需要注入 ServiceA
if (dependency instanceof ServiceA) {
    // 2. 尝试从容器获取 ServiceA
    ServiceA serviceA = getBean("serviceA");
}

此时 Spring 的处理逻辑:

  1. 检查 singletonObjects:无 ServiceA(未完全初始化)。

  2. 检查 earlySingletonObjects:无 ServiceA

  3. 从 singletonFactories 获取 ObjectFactory

    ObjectFactory<?> singletonFactory = singletonFactories.get("serviceA");
    ServiceA serviceA = (ServiceA) singletonFactory.getObject();
  4. 将 ServiceA 存入 earlySingletonObjects(标记为提前暴露):

    earlySingletonObjects.put("serviceA", serviceA);
    singletonFactories.remove("serviceA");
  • 缓存状态

    singletonObjects: {"serviceB": ServiceB@5678}
     
    earlySingletonObjects: {"serviceA": ServiceA@1234}
 步骤 5:完成 ServiceB 的初始化
// 1. 将 ServiceA 的引用注入 ServiceB
serviceB.setServiceA(serviceA);

// 2. 执行 ServiceB 的初始化(@PostConstruct等)
initBean(serviceB);

// 3. 将完全初始化的 ServiceB 存入 singletonObjects
singletonObjects.put("serviceB", serviceB);
singletonFactories.remove("serviceB");   
  • 缓存状态

    singletonObjects: {"serviceB": ServiceB@5678}
    
    earlySingletonObjects: {"serviceA": ServiceA@1234}
步骤 6:完成 ServiceA 的初始化 
// 1. 将 ServiceB 的引用注入 ServiceA
serviceA.setServiceB(serviceB);

// 2. 执行 ServiceA 的初始化(@PostConstruct等)
initBean(serviceA);

// 3. 将完全初始化的 ServiceA 存入 singletonObjects
singletonObjects.put("serviceA", serviceA);
earlySingletonObjects.remove("serviceA");
  • 最终缓存状态:

    singletonObjects: {"serviceA": ServiceA@1234, "serviceB": ServiceB@5678}
    
    earlySingletonObjects: {}
    
    singletonFactories: {}

 3. 关键代码逻辑(简化版)

// AbstractAutowireCapableBeanFactory
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
    // 1. 实例化 Bean
    Object bean = createBeanInstance(beanName, mbd, args);

    // 2. 将 Bean 的 ObjectFactory 存入 singletonFactories(三级缓存)
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

    // 3. 属性注入(可能触发循环依赖)
    populateBean(beanName, mbd, instanceWrapper);

    // 4. 初始化 Bean(@PostConstruct等)
    initializeBean(beanName, exposedObject, mbd);

    return exposedObject;
}

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
        }
    }
}

4. 为什么需要三级缓存?

缓存层级解决的核心问题
singletonFactories存储生成代理对象的逻辑(如 AOP),确保循环依赖时返回的是最终代理对象,而非原始对象。
earlySingletonObjects避免重复执行 ObjectFactory.getObject()(如多次 AOP 代理)。
singletonObjects存储完全初始化后的 Bean,避免重复创建。

经典场景
        如果 Bean 需要 AOP 代理,singletonFactories 会存储生成代理的逻辑,确保循环依赖时注入的是代理对象,而非原始对象。

 5. 循环依赖的支持限制 

依赖类型是否支持原因
构造器循环依赖❌ 不支持实例化前无法提前暴露引用(未进入三级缓存)
Setter/字段循环依赖✅ 支持实例化后可通过三级缓存暴露半成品 Bean
原型(Prototype)循环依赖❌ 不支持Spring 不缓存原型 Bean,无法提前暴露引用

6. 如何避免循环依赖? 

  • 设计层面

    • 避免双向依赖,改用单向依赖。

    • 使用 @Lazy 延迟加载。

  • 代码层面

    • 用构造器注入 + @Autowired(required=false)

    • 提取公共逻辑到第三方类。


 7.  总结

Spring 的三级缓存机制通过 提前暴露半成品 Bean 解决了单例模式的循环依赖问题:

  • singletonFactories:存储 Bean 工厂,用于处理代理对象

  • earlySingletonObjects:缓存提前暴露的 Bean,避免重复创建。

  • singletonObjects:存储最终可用的 Bean。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值