Spring循环依赖最全详解(原理+流程+方案)

Spring循环依赖是大厂经常涉及的,下面我详解Spring循环依赖@mikechen

Spring循环依赖

在 Spring 框架中,循环依赖是一个较为典型且重要的问题。

所谓循环依赖,指的是多个 Bean 之间相互依赖,形成闭环,例如 A 依赖 B,B 又依赖 A。

Spring循环依赖最全详解(原理+流程+方案)-mikechen

A -> B
B -> A

Spring 循环依赖本质上是:两个或多个 Bean 在创建过程中互相需要对方,导致“我等你、你等我”的死锁式依赖。

 

Spring循环依赖解决

Spring 解决循环依赖的核心:不是“消灭依赖”,而是提前暴露一个“半成品引用”。

让后创建的 Bean 先拿到这个引用继续完成创建,等第一个 Bean 后续初始化完成后,再把完整对象放回容器里 。

Spring循环依赖最全详解(原理+流程+方案)-mikechen

真正支撑这套机制的是三级缓存:

其中,一级缓存保存完整 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,用于按需创建早期引用或代理对象

大致流程如下:

Spring循环依赖最全详解(原理+流程+方案)-mikechen

当 A 依赖 B,B 依赖 A 时,流程如下:

  1. 实例化 A:在三级缓存中存入 A 的 ObjectFactory

  2. 填充 A 的属性:发现依赖 B,触发 B 的加载。

  3. 实例化 B:在三级缓存中存入 B 的 ObjectFactory

  4. 填充 B 的属性:发现依赖 A。此时 B 去缓存中找 A,通过三级缓存的工厂拿到 A 的“半成品”引用,并将 A 移入二级缓存。

  5. B 完成初始化:进入一级缓存。

  6. A 拿到 B:继续完成自身初始化,进入一级缓存。

需要注意的是,Spring 并不能解决所有循环依赖问题。

对于构造器注入形成的循环依赖,Spring 通常无法处理,因为构造器调用必须在实例化阶段完成,无法提前暴露引用。

因此,实际开发中应尽量避免设计强耦合的循环依赖结构。

优先采用接口解耦、事件驱动或延迟加载等方式优化设计。

评论交流
    说说你的看法