2025年Java面试宝典下载链接(提取码:9b3g)
Spring 是如何解决 Bean 的循环依赖

在 Java 面试中,Spring 框架的循环依赖问题几乎必考。很多同学被问到"Spring 怎么解决 Bean 的循环依赖"时,虽然能说出三级缓存的概念,但具体细节往往讲不清楚。今天我们就从源码设计角度,拆解 Spring 解决循环依赖的核心机制,帮你彻底掌握这个高频考点。
什么是循环依赖?
假设有两个 Bean:UserService 依赖 OrderService,而 OrderService 又反过来依赖 UserService,这就形成了循环依赖。如果用传统方式直接创建,程序会陷入死循环,最终导致堆栈溢出。Spring 的解决方式非常巧妙,核心是通过提前暴露半成品对象来实现解耦。
三级缓存如何运作?
Spring 通过三级缓存来管理 Bean 的创建过程:
- 一级缓存(singletonObjects):存放完全初始化好的 Bean
- 二级缓存(earlySingletonObjects):存放早期暴露的 Bean(已实例化但未初始化)
- 三级缓存(singletonFactories):存放 Bean 的工厂对象
当遇到循环依赖时,Spring 会这样处理:
- 创建 UserService 实例(此时还未初始化)
- 将 UserService 的工厂对象存入三级缓存
- 开始注入 OrderService 时,发现需要 UserService
- 从三级缓存获取工厂对象,生成早期引用
- 将早期引用提升到二级缓存
- OrderService 完成初始化后,UserService 再继续完成属性注入

为什么不直接用二级缓存?
这个问题经常被面试官追问。关键在于 AOP 代理的处理:
- 如果 Bean 需要代理,Spring 必须保证最终返回的是代理对象
- 三级缓存中的 ObjectFactory 可以延迟生成代理对象
- 如果只有二级缓存,可能在循环依赖时提前生成错误的对象
举个实际例子:
当 Bean A 依赖 Bean B,而两者都需要代理时,通过三级缓存可以确保最终的代理对象被正确注入,避免出现"代理套娃"的问题。
哪些情况无法解决循环依赖?
虽然三级缓存机制很强大,但仍有三种情况无法解决:
- 构造器注入的循环依赖:在实例化阶段就需要完成依赖注入,此时无法提前暴露引用
- prototype 作用域的 Bean:Spring 不管理 prototype Bean 的生命周期
- @Async 等基于代理的注解:某些特殊代理场景下可能失效

高频面试问题整理
- 三级缓存分别存储什么?为什么需要三级而不是两级?
- 循环依赖解决过程中,Bean 会经历哪些状态变化?
- Spring 如何保证 AOP 代理对象在循环依赖时的正确性?
- 为什么构造器注入无法解决循环依赖?
- 如果自己实现循环依赖解决,你会考虑哪些关键点?
如果大家需要购买面试鸭会员,可以通过面试鸭返利网找到我,下单立返 25 元。这里还有更多 Java 面试真题解析和实战技巧分享,助你轻松应对大厂技术面!


