【知识点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引用
四、关键设计点
-
提前暴露引用:
- 在对象实例化后立即暴露引用(三级缓存)
- 打破循环链条
-
代理对象处理:
- 三级缓存存储的是ObjectFactory而非直接对象
- 可以处理需要AOP代理的情况
protected Object getEarlyBeanReference(String beanName, Object bean) { // 如果需要代理,在这里返回代理对象 return wrapIfNecessary(bean); }
为什么AOP代理需要特殊处理?
在普通情况下:
- Bean初始化完成后才创建代理(通过
BeanPostProcessor
) - 但循环依赖要求在初始化完成前就暴露Bean引用
如果不特殊处理,会导致:
- 注入的是原始对象而非代理对象
- 后续AOP增强失效
- 业务逻辑出现不一致
三级缓存如何解决代理问题
核心机制
// 在DefaultSingletonBeanRegistry中
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
// 将工厂对象放入三级缓存
this.singletonFactories.put(beanName, singletonFactory);
// ...
}
这个ObjectFactory
的关键实现:
() -> getEarlyBeanReference(beanName, mbd, bean)
处理流程(以A→B→A为例,且A需要代理)
-
首次创建Bean A
- 实例化原始A对象
- 将A的
ObjectFactory
放入三级缓存
addSingletonFactory("A", () -> getEarlyBeanReference("A", mbd, rawA));
-
当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
) - 将代理对象放入二级缓存
- 从三级缓存获取A的
-
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代理放入一级缓存
- 缓存升级机制:
- 对象状态变化:三级 → 二级 → 一级
- 防止重复创建代理对象
五、不同作用域的支持情况
作用域 | 是否支持循环依赖 | 原因 |
---|---|---|
Singleton | 支持 | 有完整的三级缓存机制 |
Prototype | 不支持 | 不缓存bean实例 |
Request/Session | 不支持 | 生命周期短且非单例 |
六、常见问题解决方案
-
构造器循环依赖:
- 无法解决(必须使用setter注入)
- 因为对象尚未创建,无法提前暴露引用
-
多例循环依赖:
- 无法解决(每次getBean都创建新实例)
-
异步@Async方法:
- 需要额外处理(可能破坏代理链)