当前位置: 首页 > news >正文

【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在二级缓存的早期引用

二级缓存的核心作用

  1. 缓存早期代理对象

    • 当通过三级缓存的ObjectFactory生成早期引用后,将其存入二级缓存
    • 避免重复执行ObjectFactory.getObject()(可能涉及复杂的代理生成逻辑)
  2. 解决多级依赖中的重复代理问题

    场景:A → B → C → A
    - 当C获取A的引用时,直接从二级缓存获取已生成的代理对象
    - 保证整个依赖链中的A引用是同一个代理实例
    
  3. 防止并发创建时的重复实例化

    // 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代理的兼容性
  • 线程安全的保证

相关文章:

  • 如何使用智能化RFID管控系统,对涉密物品进行安全有效的管理?
  • 在香橙派5 NPU上使用Yolov5
  • Ollama+Deepseek+chatbox快速部署属于自己的大模型
  • SSM课设-学生选课系统
  • 格式工厂 FormatFactory v5.18.便携版 ——多功能媒体文件转换工具
  • 玄机——第一章 应急响应-Linux入侵排查
  • 在 Go 中实现事件溯源:构建高效且可扩展的系统
  • Jupyter lab 无法导出格式 Save and Export Notebook As无法展开
  • CSS实现单行、多行文本溢出显示省略号(…)
  • JVM 类加载机制
  • QT无弹窗运行和只允许运行一个exe
  • 问卷数据分析|SPSS实操之独立样本T检验
  • Reached heap limit Allocation failed - JavaScript heap out of memory
  • git 记录
  • 用大模型学大模型03-数学基础 概率论 条件概率 全概率公式 贝叶斯定理
  • 9种慢慢被淘汰的编程语言...
  • 【Prometheus】prometheus黑盒监控balckbox全面解析与应用实战
  • easyexcel快速使用
  • H5接入支付宝手机网站支付并实现
  • 百度宣布:免费!
  • 欧盟公布关税反制清单,瞄准美国飞机、汽车等产品
  • 国家主席习近平同普京总统签署关于进一步深化中俄新时代全面战略协作伙伴关系的联合声明
  • A股低开高走全线上涨:军工股再度领涨,两市成交12934亿元
  • 常州市委原常委、组织部部长陈翔调任江苏省民宗委副主任
  • 李干杰走访各民主党派中央和全国工商联机关
  • 外交部:解放军参加红场阅兵体现了中方对历史的尊重和铭记