Spring 单例 Bean 三级缓存(源码级详解 + 流程 + 作用 + 面试要点)
目录一、先明确前提二、三级缓存定义源码三个 Map1. 一级缓存singletonObjects2. 二级缓存earlySingletonObjects3. 三级缓存singletonFactories三、完整获取 Bean 流程getSingleton 核心逻辑整体执行步骤四、结合「循环依赖」实战走一遍最经典场景场景分步流程步骤 1开始创建 Bean A步骤 2开始创建 Bean B步骤 3再次获取 Bean A循环依赖触发步骤 4B 完成属性填充 初始化步骤 5A 完成创建最终状态五、核心问题解答面试高频1. 为什么需要三级缓存只用一级 二级不行吗2. 为什么构造器注入的循环依赖无法解决3. 二级缓存的作用是什么4. 原型 Bean 为什么没有循环依赖问题5. 三级缓存各自什么时候存、什么时候删六、精简总结背诵版Spring单例 Bean 三级缓存核心目的解决单例 Bean 循环依赖只针对singleton、非构造器注入的场景生效。一、先明确前提只作用于单例 Beansingleton原型 Bean 不缓存、不存在循环依赖解决方案。构造器注入循环依赖无法被三级缓存解决会直接抛异常 仅支持setter 注入 / 字段注入Autowired的循环依赖。三级缓存都存在于DefaultSingletonBeanRegistry类中。二、三级缓存定义源码三个 Map1. 一级缓存singletonObjects// 完整、初始化完成的单例 Bean成品 private final MapString, Object singletonObjects new ConcurrentHashMap(256);作用存放完全创建好、属性填充、初始化全部完成的单例 Bean。业务正常使用时优先从这里拿 Bean。特征成品 Bean对外提供服务。2. 二级缓存earlySingletonObjects// 提前曝光的半成品 Bean已实例化、未完成属性填充初始化 private final MapString, Object earlySingletonObjects new HashMap(16);作用存放已经实例化new 出来但还没完成属性注入、初始化的半成品 Bean。专门用来提前暴露实例阻断循环依赖。特征裸对象还没走完完整 Bean 生命周期。3. 三级缓存singletonFactories// Bean 工厂对象ObjectFactory用来生成半成品 Bean private final MapString, 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)整体执行步骤先查一级缓存singletonObjects存在直接返回完整 Bean流程结束。不存在进入下一步。判断当前 Bean 是否正在创建中Spring 用singletonsCurrentlyInCreation集合标记「正在创建的 Bean」。不在创建中说明首次创建继续走实例化流程。正在创建中说明出现循环依赖进入二级、三级缓存查找。查询二级缓存earlySingletonObjects存在直接返回半成品 Bean解决循环依赖。不存在进入三级缓存。查询三级缓存singletonFactories取出ObjectFactory工厂对象调用getObject()生成半成品 Bean可能是代理对象。将生成的 Bean存入二级缓存。从三级缓存中删除当前 BeanName三级工厂只临时用一次。返回该半成品 Bean。Bean 完整创建完毕后 最终把完整 Bean 放入一级缓存同时清空二、三级缓存中对应数据。四、结合「循环依赖」实战走一遍最经典场景场景Bean A 依赖 Bean BBean B 依赖 Bean A均为singleton Autowired 字段注入可被三级缓存解决分步流程步骤 1开始创建 Bean A查一级缓存无。标记A为正在创建加入singletonsCurrentlyInCreation。实例化 Anew A()→半成品 A 诞生。把 A 的 ObjectFactory 放入三级缓存singletonFactories支持 AOP 代理。开始属性填充发现 A 依赖 B触发getBean(B)。步骤 2开始创建 Bean B查一级缓存无。标记B为正在创建。实例化 B → 半成品 B。把 B 的 ObjectFactory 放入三级缓存。属性填充B 依赖 A触发getBean(A)。步骤 3再次获取 Bean A循环依赖触发查一级缓存无。发现A 正在创建中→ 判定循环依赖。查二级缓存无。查三级缓存拿到 A 的ObjectFactory。执行getObject()得到半成品 A可能是代理对象。将半成品 A 存入二级缓存并删除三级缓存中的 A。返回半成品 A 给 B。步骤 4B 完成属性填充 初始化B 拿到 A 后完成所有属性注入、初始化、后置处理。完整 B 存入一级缓存二、三级清理 B 相关数据。 B 创建完成回到 A 的属性填充流程。步骤 5A 完成创建A 成功拿到完整 B完成属性填充、初始化。完整 A 存入一级缓存清理二、三级缓存中 A。最终状态一级缓存A、B 两个完整单例 Bean二级、三级缓存无数据循环依赖完美解决。五、核心问题解答面试高频1. 为什么需要三级缓存只用一级 二级不行吗不行核心原因AOP 动态代理如果只有一、二级缓存 Bean 实例化后直接丢进二级缓存此时原始对象固定。 若该 Bean 需要 AOP 代理循环依赖时注入的是原对象不是代理对象业务出错。三级缓存存的是ObjectFactory 调用getObject()时会执行SmartInstantiationAwareBeanPostProcessor动态生成代理对象。 保证循环依赖中注入的也是代理 Bean。一句话三级缓存是为了兼容 AOP 代理。2. 为什么构造器注入的循环依赖无法解决构造器注入是实例化阶段就需要依赖创建 A → 执行构造方法直接需要 B。创建 B → 执行构造方法直接需要 A。连实例都 new 不出来根本走不到「放入三级缓存」那一步。 Spring 提前实例化半成品的逻辑失效直接抛出BeanCurrentlyInCreationException。3. 二级缓存的作用是什么三级工厂只使用一次生成对象后立刻移入二级缓存。若存在多条依赖链同时获取该半成品 Bean直接从二级取避免重复执行 ObjectFactory 生成对象提升效率。4. 原型 Bean 为什么没有循环依赖问题原型 Bean 每次getBean都会新建对象不做任何缓存自然不存在循环引用卡死。5. 三级缓存各自什么时候存、什么时候删三级缓存Bean 刚实例化后存入生成对象移入二级后立即删除。二级缓存循环依赖时临时存放半成品Bean 完整创建后删除。一级缓存Bean 完全初始化完毕后存入容器销毁时才清除。六、精简总结背诵版三级缓存结构1 级singletonObjects完整成品单例 Bean2 级earlySingletonObjects提前曝光的半成品 Bean3 级singletonFactories生成 Bean / 代理的 ObjectFactory 工厂核心作用整体解决单例 Bean setter / 字段注入 的循环依赖三级缓存专门适配AOP 代理。查找顺序一级 → 二级 → 三级循环依赖触发二、三级使用。限制构造器注入循环依赖、原型 Bean 不受这套机制管辖。