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

Spring 中的三级缓存机制详解

🤟致敬读者

  • 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉

📘博主相关

  • 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息

文章目录

    • Spring 中的三级缓存机制详解
      • 一、三级缓存定义(在 `DefaultSingletonBeanRegistry` 中)
      • 二、解决循环依赖的核心流程(以 A→B→A 为例)
        • 1. 创建 Bean A
        • 2. 创建 Bean B
        • 3. 完成 Bean A
      • 三、关键源码解析
        • 1. 从缓存获取 Bean 的优先级 (`getSingleton()`)
        • 2. 曝光早期引用 (`addSingletonFactory()`)
      • 四、三级缓存的本质作用
      • 五、高频面试问题详解
        • 1. 为什么需要三级缓存?二级不行吗?
        • 2. 哪些场景无法解决循环依赖?
        • 3. 如何证明三级缓存的存在?
      • 六、典型循环依赖解决方案对比
      • 七、面试实战回答模板


📃文章前言

  • 🔷文章均为学习工作中整理的笔记。
  • 🔶如有错误请指正,共同学习进步。

Spring 中的三级缓存机制详解

在这里插入图片描述

Spring 的三级缓存机制是解决循环依赖问题的核心设计,也是面试中高频考点。下面从原理、流程、源码三个维度深入解析:


一、三级缓存定义(在 DefaultSingletonBeanRegistry 中)

// 1. 一级缓存:存放完全初始化好的Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);// 2. 二级缓存:存放早期曝光对象(已实例化但未初始化)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);// 3. 三级缓存:存放单例工厂(用于生成代理对象)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

二、解决循环依赖的核心流程(以 A→B→A 为例)

1. 创建 Bean A
实例化A
将A的ObjectFactory放入三级缓存
填充A的属性 - 发现依赖B
2. 创建 Bean B
graph TDB1[实例化B] --> B2[将B的ObjectFactory放入三级缓存]B2 --> B3[填充B的属性 - 发现依赖A]B3 --> B4[从三级缓存获取A的ObjectFactory]B4 --> B5[调用getObject()获取A的早期引用]B5 --> B6[将A从三级缓存升级到二级缓存]
3. 完成 Bean A
B创建完成
将B注入A
执行A的初始化方法
将A放入一级缓存
清除二级缓存

三、关键源码解析

1. 从缓存获取 Bean 的优先级 (getSingleton())
protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 1. 从一级缓存查询Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {// 2. 从二级缓存查询singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {// 3. 从三级缓存获取ObjectFactoryObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 创建早期引用(可能生成代理对象)singletonObject = singletonFactory.getObject();// 升级到二级缓存this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;
}
2. 曝光早期引用 (addSingletonFactory())
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {// 将ObjectFactory放入三级缓存this.singletonFactories.put(beanName, singletonFactory);// 清除二级缓存中的旧数据this.earlySingletonObjects.remove(beanName);}}
}

四、三级缓存的本质作用

缓存级别存储内容核心作用
一级缓存完全初始化好的 Bean提供最终可用对象
二级缓存早期曝光对象(未初始化)避免重复创建代理对象
三级缓存ObjectFactory分离实例化与初始化,支持AOP

五、高频面试问题详解

1. 为什么需要三级缓存?二级不行吗?
  • 关键原因:处理 AOP 代理对象的循环依赖
  • 二级缓存缺陷
    // 若只有二级缓存:
    A 实例化 → 放入二级缓存 → 填充属性依赖 B
    B 实例化 → 从二级缓存拿到原始对象 A(非代理)→ 完成初始化
    A 继续初始化 → 生成代理对象 → 导致 B 持有的是原始对象(非代理)
    
  • 三级缓存解决方案
    通过 ObjectFactory.getObject() 动态决定返回原始对象还是代理对象
2. 哪些场景无法解决循环依赖?
场景原因
构造器注入实例化前就需要完整依赖,无法提前曝光对象
原型(prototype)Spring 不缓存原型 Bean
@Async 注解代理对象在初始化后生成,无法通过 ObjectFactory 提前创建
自定义初始化某些 InitializingBean 实现可能依赖完整状态
3. 如何证明三级缓存的存在?

通过调试查看容器状态:

// 查看缓存内容
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Field singletonObjects = beanFactory.getClass().getDeclaredField("singletonObjects");
singletonObjects.setAccessible(true);
Map<String, Object> cache = (Map) singletonObjects.get(beanFactory);
System.out.println("一级缓存: " + cache.keySet());

六、典型循环依赖解决方案对比

方案优点缺点
三级缓存Spring原生支持,自动处理无法解决构造器注入
Setter注入兼容三级缓存机制代码侵入性强
@Lazy注解延迟加载打破循环可能引发NPE问题
重构代码彻底解决设计问题业务改造成本高

七、面试实战回答模板

面试官:请解释Spring如何解决循环依赖?

回答

Spring 通过三级缓存机制解决单例Bean的循环依赖问题:

  1. 当Bean实例化后,会将其ObjectFactory存入三级缓存
  2. 依赖注入时若发现循环依赖,通过ObjectFactory.getObject()获取早期引用
  3. 获取到的对象会升级到二级缓存
  4. Bean完全初始化后放入一级缓存

关键点在于:

  • 三级缓存的ObjectFactory支持AOP代理的动态生成
  • 避免了构造器注入和原型Bean的循环依赖
  • 通过缓存分离实例化和初始化过程

源码体现在DefaultSingletonBeanRegistry的getSingleton()方法中…

掌握三级缓存机制需要深入理解Spring生命周期,建议结合源码调试加深理解(关键类:AbstractAutowireCapableBeanFactoryDefaultSingletonBeanRegistry)。


📜文末寄语

  • 🟠关注我,获取更多内容。
  • 🟡技术动态、实战教程、问题解决方案等内容持续更新中。
  • 🟢《全栈知识库》技术交流和分享社区,集结全栈各领域开发者,期待你的加入。
  • 🔵​加入开发者的《专属社群》,分享交流,技术之路不再孤独,一起变强。
  • 🟣点击下方名片获取更多内容🍭🍭🍭👇

相关文章:

  • 二叉数-100.相同的树-力扣(LeetCode)
  • 2025年U盘数据恢复软件推荐:找回丢失文件的得力助手
  • 人脸识别技术应用备案办理指南
  • protues仿真+C51+外部中断
  • triton学习笔记7: GEMM相关
  • SDC命令详解:使用set_max_area命令进行约束
  • Linux 环境配置
  • Java后端检查空条件查询
  • linux库(AI回答)
  • 算法打卡第18天
  • Java求职者面试指南:计算机基础与源码原理深度解析
  • 2000-2020年各省第三产业增加值占GDP比重数据
  • ffmpeg(四):滤镜命令
  • VS Code扩展安装后如何管理
  • 循环变量捕获问题​​
  • Java网络编程:构建现代分布式应用的核心技术
  • OPENCV图形计算面积、弧长API讲解(2)
  • 论文MR-SVD
  • 从菜鸟到骑士:TypeScript 基础修炼手册
  • 网盘变硬盘挂载软件:百度 / 阿里 / OneDrive 秒变本地磁盘
  • 仙游哪里可以做网站的/上海短视频seo优化网站
  • 餐饮网站建设方案书/seo关键词布局技巧
  • 贵州新闻网站网络推广/济南疫情最新消息
  • 兰州网站建设q.479185700棒/自动发帖软件
  • 建立网站费用较低/谷歌引擎搜索
  • 提供企业网站建设公司/建网站需要什么条件