news 2026/6/11 22:47:38

Spring 单例 Bean 三级缓存(源码级详解 + 流程 + 作用 + 面试要点)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring 单例 Bean 三级缓存(源码级详解 + 流程 + 作用 + 面试要点)

目录

一、先明确前提

二、三级缓存定义(源码三个 Map)

1. 一级缓存:singletonObjects

2. 二级缓存:earlySingletonObjects

3. 三级缓存:singletonFactories

三、完整获取 Bean 流程(getSingleton 核心逻辑)

整体执行步骤

四、结合「循环依赖」实战走一遍(最经典场景)

场景

分步流程

步骤 1:开始创建 Bean A

步骤 2:开始创建 Bean B

步骤 3:再次获取 Bean A(循环依赖触发)

步骤 4:B 完成属性填充 & 初始化

步骤 5:A 完成创建

最终状态

五、核心问题解答(面试高频)

1. 为什么需要三级缓存?只用一级 + 二级不行吗?

2. 为什么构造器注入的循环依赖无法解决?

3. 二级缓存的作用是什么?

4. 原型 Bean 为什么没有循环依赖问题?

5. 三级缓存各自什么时候存、什么时候删?

六、精简总结(背诵版)


Spring单例 Bean 三级缓存核心目的:解决单例 Bean 循环依赖,只针对singleton、非构造器注入的场景生效。

一、先明确前提

  1. 只作用于单例 Bean(singleton),原型 Bean 不缓存、不存在循环依赖解决方案。
  2. 构造器注入循环依赖无法被三级缓存解决,会直接抛异常; 仅支持:setter 注入 / 字段注入(@Autowired)的循环依赖。
  3. 三级缓存都存在于DefaultSingletonBeanRegistry类中。

二、三级缓存定义(源码三个 Map)

1. 一级缓存:singletonObjects

// 完整、初始化完成的单例 Bean(成品) private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  • 作用:存放完全创建好、属性填充、初始化全部完成的单例 Bean。
  • 业务正常使用时,优先从这里拿 Bean。
  • 特征:成品 Bean,对外提供服务。

2. 二级缓存:earlySingletonObjects

// 提前曝光的半成品 Bean(已实例化、未完成属性填充+初始化) private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
  • 作用:存放已经实例化(new 出来),但还没完成属性注入、初始化半成品 Bean
  • 专门用来提前暴露实例,阻断循环依赖。
  • 特征:裸对象,还没走完完整 Bean 生命周期。

3. 三级缓存:singletonFactories

// Bean 工厂对象(ObjectFactory),用来生成半成品 Bean private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  • 存放的不是 Bean 本身,而是「Bean 工厂」ObjectFactory函数式接口)。
  • 核心价值支持 AOP 动态代理。 如果 Bean 需要被 AOP 代理,ObjectFactory.getObject()返回代理对象,而非原实例。
  • 只有刚实例化、还没放入二级缓存时,才会存在于此。

总结三级角色: 1 级:成品 Bean2 级:普通半成品 Bean3 级:生成半成品 / 代理 Bean 的工厂


三、完整获取 Bean 流程(getSingleton 核心逻辑)

Spring 获取单例 Bean 严格遵循1 级 → 2 级 → 3 级顺序查找,源码入口:DefaultSingletonBeanRegistry#getSingleton(String beanName, boolean allowEarlyReference)

整体执行步骤

  1. 先查一级缓存singletonObjects

    • 存在:直接返回完整 Bean,流程结束。
    • 不存在:进入下一步。
  2. 判断当前 Bean 是否正在创建中Spring 用singletonsCurrentlyInCreation集合标记「正在创建的 Bean」。

    • 不在创建中:说明首次创建,继续走实例化流程。
    • 正在创建中:说明出现循环依赖,进入二级、三级缓存查找。
  3. 查询二级缓存earlySingletonObjects

    • 存在:直接返回半成品 Bean,解决循环依赖。
    • 不存在:进入三级缓存。
  4. 查询三级缓存singletonFactories

    • 取出ObjectFactory工厂对象,调用getObject()生成半成品 Bean(可能是代理对象)
    • 将生成的 Bean存入二级缓存
    • 三级缓存中删除当前 BeanName(三级工厂只临时用一次)。
    • 返回该半成品 Bean。
  5. Bean 完整创建完毕后 最终把完整 Bean 放入一级缓存,同时清空二、三级缓存中对应数据。


四、结合「循环依赖」实战走一遍(最经典场景)

场景

  • Bean A 依赖 Bean B
  • Bean B 依赖 Bean A
  • 均为singleton + @Autowired 字段注入(可被三级缓存解决)

分步流程

步骤 1:开始创建 Bean A
  1. 一级缓存:无。
  2. 标记A正在创建(加入singletonsCurrentlyInCreation)。
  3. 实例化 A(new A())→半成品 A 诞生
  4. 把 A 的 ObjectFactory 放入三级缓存singletonFactories(支持 AOP 代理)。
  5. 开始属性填充:发现 A 依赖 B,触发getBean(B)
步骤 2:开始创建 Bean B
  1. 一级缓存:无。
  2. 标记B正在创建
  3. 实例化 B → 半成品 B。
  4. 把 B 的 ObjectFactory 放入三级缓存
  5. 属性填充:B 依赖 A,触发getBean(A)
步骤 3:再次获取 Bean A(循环依赖触发)
  1. 一级缓存:无。
  2. 发现A 正在创建中→ 判定循环依赖。
  3. 二级缓存:无。
  4. 三级缓存:拿到 A 的ObjectFactory
  5. 执行getObject()得到半成品 A(可能是代理对象)
  6. 半成品 A 存入二级缓存,并删除三级缓存中的 A
  7. 返回半成品 A 给 B。
步骤 4:B 完成属性填充 & 初始化

B 拿到 A 后,完成所有属性注入、初始化、后置处理。完整 B 存入一级缓存,二、三级清理 B 相关数据。 B 创建完成,回到 A 的属性填充流程。

步骤 5:A 完成创建

A 成功拿到完整 B,完成属性填充、初始化。完整 A 存入一级缓存,清理二、三级缓存中 A。

最终状态

  • 一级缓存:A、B 两个完整单例 Bean
  • 二级、三级缓存:无数据

循环依赖完美解决。


五、核心问题解答(面试高频)

1. 为什么需要三级缓存?只用一级 + 二级不行吗?

不行,核心原因:AOP 动态代理

  • 如果只有一、二级缓存: Bean 实例化后直接丢进二级缓存,此时原始对象固定。 若该 Bean 需要 AOP 代理,循环依赖时注入的是原对象,不是代理对象,业务出错。
  • 三级缓存存的是ObjectFactory: 调用getObject()时,会执行SmartInstantiationAwareBeanPostProcessor动态生成代理对象。 保证循环依赖中,注入的也是代理 Bean

一句话:三级缓存是为了兼容 AOP 代理

2. 为什么构造器注入的循环依赖无法解决?

构造器注入是实例化阶段就需要依赖

  1. 创建 A → 执行构造方法,直接需要 B。
  2. 创建 B → 执行构造方法,直接需要 A。
  3. 连实例都 new 不出来,根本走不到「放入三级缓存」那一步。 Spring 提前实例化半成品的逻辑失效,直接抛出BeanCurrentlyInCreationException

3. 二级缓存的作用是什么?

  • 三级工厂只使用一次,生成对象后立刻移入二级缓存。
  • 若存在多条依赖链同时获取该半成品 Bean,直接从二级取,避免重复执行 ObjectFactory 生成对象,提升效率。

4. 原型 Bean 为什么没有循环依赖问题?

原型 Bean 每次getBean都会新建对象,不做任何缓存,自然不存在循环引用卡死。

5. 三级缓存各自什么时候存、什么时候删?

  • 三级缓存:Bean 刚实例化后存入;生成对象移入二级后立即删除
  • 二级缓存:循环依赖时临时存放半成品;Bean 完整创建后删除
  • 一级缓存:Bean 完全初始化完毕后存入;容器销毁时才清除。

六、精简总结(背诵版)

  1. 三级缓存结构

    • 1 级singletonObjects:完整成品单例 Bean
    • 2 级earlySingletonObjects:提前曝光的半成品 Bean
    • 3 级singletonFactories:生成 Bean / 代理的 ObjectFactory 工厂
  2. 核心作用整体解决单例 Bean setter / 字段注入 的循环依赖;三级缓存专门适配AOP 代理

  3. 查找顺序一级 → 二级 → 三级,循环依赖触发二、三级使用。

  4. 限制构造器注入循环依赖、原型 Bean 不受这套机制管辖。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 22:43:55

STM32H750VBT6双层核心板AD设计包:原理图+PCB+器件参考+BOM建议

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;一套开箱即用的STM32H750VBT6硬件设计资源&#xff0c;基于Altium Designer开发&#xff0c;包含完整可投产的原理图&#xff08;SCH&#xff09;和双层PCB文件。电源路径经过优化&#xff0c;支持稳定宽压输入…

作者头像 李华
网站建设 2026/6/11 22:43:46

西北 AI 获客服务商科普:3 分钟看懂企业 AI 获客底层逻辑

核心结论企业 AI 获客已成为西北区域企业增长新引擎&#xff0c;陕西企来客科技等本土专业服务商通过自研 GEO 优化技术&#xff0c;可帮助企业 AI 曝光量平均提升 230%&#xff0c;获客成本降低 40%正规 AI 获客服务商需具备 ICP 经营许可证、软件著作权、技术服务资质三重核心…

作者头像 李华
网站建设 2026/6/11 22:42:55

还在为找不到想听的音乐而烦恼吗?试试这款开源音乐聚合神器

还在为找不到想听的音乐而烦恼吗&#xff1f;试试这款开源音乐聚合神器 【免费下载链接】lx-music-desktop 一个基于 Electron 的音乐软件 项目地址: https://gitcode.com/GitHub_Trending/lx/lx-music-desktop 你是否曾为了寻找一首心仪的歌曲&#xff0c;不得不在多个…

作者头像 李华
网站建设 2026/6/11 22:38:05

告别手写增删改查:飞算JavaAI高效CRUD实操,3分钟生成全套REST接口

从40分钟到3分钟&#xff0c;一个订单管理模块的效率对比很多Java后端开发者都有这样的体会&#xff1a;项目再大&#xff0c;80%的时间其实是在写CRUD——建实体、写Mapper、撸Service、套Controller&#xff0c;每个接口还得配参数校验、异常处理、Swagger注解。订单、商品、…

作者头像 李华