【Java】一文了解spring的三级缓存
三级缓存完整工作流程
创建 Bean A
├→ 1. 实例化A(调用构造函数)
│ └→ 此时A对象是原始状态,未填充属性
│
├→ 2. 将A的ObjectFactory包装对象存入三级缓存(singletonFactories)
│ └→ 代码:addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))
│
├→ 3. 开始属性注入(填充依赖)
│ └→ 发现需要注入Bean B
│
├→ 4. 触发Bean B的创建
│ ├→ 4.1 实例化B
│ ├→ 4.2 将B的ObjectFactory存入三级缓存
│ └→ 4.3 B开始属性注入,发现需要注入Bean A
│ │
│ ├→ 4.3.1 从三级缓存获取A的ObjectFactory
│ │ └→ 通过getSingleton()方法检查缓存:
│ │ ① 一级缓存:无
│ │ ② 二级缓存:无
│ │ ③ 三级缓存:存在 → 执行ObjectFactory.getObject()
│ │
│ ├→ 4.3.2 生成A的早期引用(可能经过AOP代理)
│ │ └→ 此时会将A的早期引用存入二级缓存(earlySingletonObjects)
│ │ └→ 同时删除三级缓存中的A的ObjectFactory
│ │
│ └→ 4.3.3 将A的早期引用注入给B
│
├→ 5. Bean B完成初始化
│ └→ 5.1 执行初始化回调(@PostConstruct等)
│ └→ 5.2 将完整B对象存入一级缓存(singletonObjects)
│ └→ 5.3 清除B在二、三级缓存的残留数据
│
└→ 6. Bean A继续初始化
├→ 6.1 属性注入完成(已持有B的完整引用)
├→ 6.2 执行A的初始化回调
└→ 6.3 将完整A对象存入一级缓存
└→ 同时清除A在二级缓存的早期引用
二级缓存的核心作用
-
缓存早期代理对象
- 当通过三级缓存的
ObjectFactory
生成早期引用后,将其存入二级缓存 - 避免重复执行
ObjectFactory.getObject()
(可能涉及复杂的代理生成逻辑)
- 当通过三级缓存的
-
解决多级依赖中的重复代理问题
场景:A → B → C → A - 当C获取A的引用时,直接从二级缓存获取已生成的代理对象 - 保证整个依赖链中的A引用是同一个代理实例
-
防止并发创建时的重复实例化
// getSingleton() 方法源码片段 protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 关键点:先检查二级缓存 singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { // 最后才从三级缓存获取 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); // 存入二级缓存 this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
为什么需要三级缓存而不是两级?
通过一个 AOP代理场景 说明:
1. 创建原始对象A → 存入三级缓存(ObjectFactory)
2. B依赖A时:
- 通过ObjectFactory.getObject() 生成A的代理对象A@Proxy
- 将A@Proxy存入二级缓存
3. C也依赖A时:
- 直接从二级缓存获取A@Proxy
- 避免再次通过ObjectFactory创建不同代理
如果只有两级缓存:
- 需要将原始对象和代理对象都放在二级缓存
- 会导致对象状态管理混乱
设计验证实验
实验代码:
@Component
public class DebugBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
System.out.println("生成早期引用: " + beanName);
return bean; // 此处可插入代理逻辑
}
}
观察日志:
1. 创建A → 存入三级缓存
2. B获取A引用 → 触发getEarlyBeanReference()
3. 将A代理存入二级缓存
4. 后续其他Bean获取A时,直接使用二级缓存
总结设计思想
缓存级别 | 解决问题 | 设计意图 |
---|---|---|
三级缓存 | 延迟代理对象的生成时机 | 解耦实例化与代理过程 |
二级缓存 | 缓存已生成的早期引用 | 保证依赖注入的一致性 |
一级缓存 | 存储最终可用对象 | 提供完全初始化的Bean |
这种三级缓存机制完美平衡了:
- 对象创建顺序的灵活性
- 循环依赖的解决能力
- AOP代理的兼容性
- 线程安全的保证