Spring循环依赖是大厂经常涉及的,下面我详解Spring循环依赖@mikechen
Spring循环依赖
在 Spring 框架中,循环依赖是一个较为典型且重要的问题。
所谓循环依赖,指的是多个 Bean 之间相互依赖,形成闭环,例如 A 依赖 B,B 又依赖 A。

A -> B B -> A
Spring 循环依赖本质上是:两个或多个 Bean 在创建过程中互相需要对方,导致“我等你、你等我”的死锁式依赖。
Spring循环依赖解决
Spring 解决循环依赖的核心:不是“消灭依赖”,而是提前暴露一个“半成品引用”。
让后创建的 Bean 先拿到这个引用继续完成创建,等第一个 Bean 后续初始化完成后,再把完整对象放回容器里 。

真正支撑这套机制的是三级缓存:
其中,一级缓存保存完整 Bean,二级缓存保存早期引用,三级缓存保存对象工厂。
// 一级缓存:完成品(完全初始化好的 Bean) private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 二级缓存:早期暴露的 Bean(实例化但未初始化) private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 三级缓存:ObjectFactory,用于生成早期引用(支持 AOP 等) private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
| 缓存层级 | 作用 |
|---|---|
一级缓存 singletonObjects |
存放完全创建好的单例 |
二级缓存 earlySingletonObjects |
存放提前暴露的早期引用,通常用于解决循环依赖 |
三级缓存 singletonFactories |
存放 ObjectFactory,用于按需创建早期引用或代理对象 |
大致流程如下:

当 A 依赖 B,B 依赖 A 时,流程如下:
-
实例化 A:在三级缓存中存入 A 的
ObjectFactory。 -
填充 A 的属性:发现依赖 B,触发 B 的加载。
-
实例化 B:在三级缓存中存入 B 的
ObjectFactory。 -
填充 B 的属性:发现依赖 A。此时 B 去缓存中找 A,通过三级缓存的工厂拿到 A 的“半成品”引用,并将 A 移入二级缓存。
-
B 完成初始化:进入一级缓存。
-
A 拿到 B:继续完成自身初始化,进入一级缓存。
需要注意的是,Spring 并不能解决所有循环依赖问题。
对于构造器注入形成的循环依赖,Spring 通常无法处理,因为构造器调用必须在实例化阶段完成,无法提前暴露引用。
因此,实际开发中应尽量避免设计强耦合的循环依赖结构。
优先采用接口解耦、事件驱动或延迟加载等方式优化设计。