【知识点002】Spring如何通过三级缓存解决缓存依赖问题

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

一、循环依赖的本质问题

循环依赖是指两个或多个Bean相互依赖形成闭环:

A → B → A

或更复杂的:

A → B → C → A

传统创建流程会导致无限递归,最终栈溢出。

二、三级缓存结构

Spring使用三个Map构成的三级缓存:

缓存级别 名称 存储内容 作用阶段
一级缓存 singletonObjects 完全初始化好的单例Bean 最终存储
二级缓存 earlySingletonObjects 早期暴露的Bean(已实例化未初始化) 解决循环依赖
三级缓存 singletonFactories Bean工厂(ObjectFactory) 创建代理对象

三、解决流程详解(以A→B→A为例)

1. 创建Bean A的流程

// 1. 开始创建A
getBean("A")

// 2. 标记A正在创建中
markBeanAsCreated("A")

// 3. 实例化A(调用构造器)
A instance = createBeanInstance("A")

// 4. 将A的ObjectFactory放入三级缓存
addSingletonFactory("A", () -> getEarlyBeanReference("A", beanDefinition, instance))

// 5. 开始填充属性(此时发现需要B)
populateBean("A") → getBean("B")

2. 创建Bean B的流程

// 1. 开始创建B
getBean("B")

// 2. 标记B正在创建中
markBeanAsCreated("B")

// 3. 实例化B
B instance = createBeanInstance("B")

// 4. 将B的ObjectFactory放入三级缓存
addSingletonFactory("B", () -> getEarlyBeanReference("B", beanDefinition, instance))

// 5. 开始填充属性(此时发现需要A)
populateBean("B") → getBean("A")

3. 再次获取A的流程

// 1. 检查一级缓存:无
getSingleton("A")

// 2. 检查二级缓存:无
// 3. 检查三级缓存:找到A的ObjectFactory

// 4. 执行工厂方法获取早期引用
A earlyA = getEarlyBeanReference("A")

// 5. 将早期引用放入二级缓存
// 6. 返回earlyA给B完成属性注入

4. 完成B的创建

// 1. B完成属性注入(已持有A的早期引用)
// 2. 执行B的初始化回调
initializeBean("B")

// 3. 将B放入一级缓存
addSingleton("B", fullyInitializedB)

// 4. 返回完全初始化的B给A

5. 完成A的创建

// 1. A继续完成属性注入(已持有完全初始化的B)
// 2. 执行A的初始化回调
initializeBean("A")

// 3. 将A放入一级缓存
addSingleton("A", fullyInitializedA)

// 4. 清理二级缓存中的A引用

四、关键设计点

  1. 提前暴露引用

    • 在对象实例化后立即暴露引用(三级缓存)
    • 打破循环链条
  2. 代理对象处理

    • 三级缓存存储的是ObjectFactory而非直接对象
    • 可以处理需要AOP代理的情况
    protected Object getEarlyBeanReference(String beanName, Object bean) {
        // 如果需要代理,在这里返回代理对象
        return wrapIfNecessary(bean);
    }
    

为什么AOP代理需要特殊处理?

在普通情况下:

  • Bean初始化完成后才创建代理(通过BeanPostProcessor
  • 但循环依赖要求在初始化完成前就暴露Bean引用

如果不特殊处理,会导致:

  1. 注入的是原始对象而非代理对象
  2. 后续AOP增强失效
  3. 业务逻辑出现不一致

三级缓存如何解决代理问题

核心机制

// 在DefaultSingletonBeanRegistry中
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    // 将工厂对象放入三级缓存
    this.singletonFactories.put(beanName, singletonFactory);
    // ...
}

这个ObjectFactory的关键实现:

() -> getEarlyBeanReference(beanName, mbd, bean)

处理流程(以A→B→A为例,且A需要代理)

  1. 首次创建Bean A

    • 实例化原始A对象
    • 将A的ObjectFactory放入三级缓存
    addSingletonFactory("A", () -> getEarlyBeanReference("A", mbd, rawA));
    
  2. 当B需要注入A时

    • 从三级缓存获取A的ObjectFactory
    • 执行getEarlyBeanReference()获取早期引用
      protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
          // 这里会调用所有SmartInstantiationAwareBeanPostProcessor
          for (BeanPostProcessor bp : getBeanPostProcessors()) {
              if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                  SmartInstantiationAwareBeanPostProcessor ibp = 
                      (SmartInstantiationAwareBeanPostProcessor) bp;
                  bean = ibp.getEarlyBeanReference(bean, beanName);
              }
          }
          return bean;
      }
      
    • AOP代理在此生成(通过AbstractAutoProxyCreator
    • 将代理对象放入二级缓存
  3. B完成初始化后

    • A继续初始化时,会再次检查是否需要代理
    • 由于已经通过getEarlyBeanReference创建过代理,会返回同一个代理对象
    • 保证整个生命周期内代理对象唯一
sequenceDiagram participant Container participant A_Original participant A_Proxy participant B Container->>A_Original: 1. 实例化原始A对象 Container->>Container: 2. 注册A的ObjectFactory到三级缓存 Container->>A_Original: 3. 开始填充A的属性(发现需要B) Container->>B: 4. 创建B实例 Container->>Container: 5. 注册B的ObjectFactory Container->>B: 6. 填充B的属性(需要A) Container->>Container: 7. 从三级缓存获取A的工厂 Container->>A_Proxy: 8. 通过getEarlyBeanReference创建A的代理 Container->>Container: 9. 将A代理放入二级缓存 Container->>B: 10. 将A代理注入B Container->>B: 11. 完成B的初始化 Container->>A_Original: 12. 继续完成A的初始化 Container->>Container: 13. 检查A已有代理,不再重复创建 Container->>Container: 14. 将A代理放入一级缓存
  1. 缓存升级机制
    • 对象状态变化:三级 → 二级 → 一级
    • 防止重复创建代理对象

五、不同作用域的支持情况

作用域 是否支持循环依赖 原因
Singleton 支持 有完整的三级缓存机制
Prototype 不支持 不缓存bean实例
Request/Session 不支持 生命周期短且非单例

六、常见问题解决方案

  1. 构造器循环依赖

    • 无法解决(必须使用setter注入)
    • 因为对象尚未创建,无法提前暴露引用
  2. 多例循环依赖

    • 无法解决(每次getBean都创建新实例)
  3. 异步@Async方法

    • 需要额外处理(可能破坏代理链)
posted @ 2025-07-04 17:05  兔麻吕  阅读(196)  评论(0)    收藏  举报