spring循环依赖通过三级缓存解决:一级缓存singletonobjects存初始化完成的bean,二级earlysingletonobjects存已确定的早期引用,三级singletonfactories存objectfactory工厂以按需生成代理,兼顾循环依赖与aop。

Spring 的循环依赖问题,核心在于“提前暴露正在创建中的 Bean”,而三级缓存是实现这一目标的关键设计。它不是为了解决所有循环依赖,而是精准支持单例 + 构造器之外注入(如 setter、@Autowired 字段)的场景。理解三级缓存,关键不在“三级”本身,而在每一级的职责和协作逻辑。
一级缓存:singletonObjects —— 存最终成品 Bean
这是最常用的单例池,存放完全初始化完毕、可直接使用的 Bean 实例。一旦 Bean 走完创建 → 属性填充 → 初始化全部流程,就会被放进来。后续所有 getBean() 请求,优先从此处取。但它不参与循环依赖的“破局”,因为等 Bean 进来时,循环引用早已报错了。
二级缓存:earlySingletonObjects —— 存早期暴露的半成品 Bean
当 A 依赖 B、B 又依赖 A 时,A 在属性注入前会把自己“提前暴露”出来,放进这个缓存。注意:这不是原始对象,而是经过 ObjectFactory 包装后、调用 getObject() 才能拿到的代理/原始实例。它的作用是让 B 在创建时能从这里拿到 A 的早期引用,继续往下走。但这个缓存只在“有循环依赖且需要提前暴露”的时候才启用,不是每次创建都放。
三级缓存:singletonFactories —— 存 ObjectFactory 工厂,决定要不要代理
这是最关键的缓存。它不存对象,而是存一个 lambda 或匿名内部类形式的 ObjectFactory,例如:
() -> getEarlyBeanReference(beanName, mbd, bean)
这个工厂的作用是:在真正需要暴露早期引用时,才动态决定——是否要加代理(比如 AOP)。如果 A 没有切面,就返回原始 bean;如果有 @Transactional 或 @Async,就在这里生成代理对象。这样既避免了提前代理带来的副作用,又保证了最终暴露出去的对象和最终单例一致。
为什么需要三级,不能只用二级?
因为 Spring 要兼顾两个需求:
• 保证循环依赖能解(需要提前暴露)
• 保证 AOP 代理生效(代理必须在初始化后、但可能在早期暴露时就要决定)
如果只有二级缓存,就得在创建 Bean 实例后立刻决定是否代理,但此时还没走到初始化步骤,无法判断是否需要增强(比如 @PostConstruct 方法里才加标记)。三级缓存把“要不要代理”延迟到第一次被其他 Bean 引用时才计算,既按需代理,又不破坏生命周期。
基本上就这些。三级缓存不是炫技,而是对“懒加载代理”和“循环依赖破局”一次精巧的兼顾。只要记住:一级存成品,二级存已确定的早期引用,三级存“将来可能生成代理”的工厂——逻辑就清晰了。










