什么是缓存依赖?
在 Spring 中,“循环依赖”通常指的是两个或多个 Bean 之间互相依赖的情况。比如:
1 2 3 4 5 6 7 8 9 10 11 12
| @Component public class A { @Autowired private B b; }
@Component public class B { @Autowired private A a; }
|
这个时候,A 依赖 B,B 又依赖 A,就形成了循环依赖。
Spring是如何解决呢?
Spring中存在三级缓存。
- 一级缓存(singletonObjects),存储Map<String, Object>,完整创建好的 Bean 实例
- 二级缓存(earlySingletonObjects),存储Map<String, Object>,提前暴露的半成品 Bean(早期引用
- 三级缓存(singletonFactories),存储Map<String, ObjectFactory<?>>,用于创建早期引用的工厂方法(可用于代理)
示例
1 2 3 4 5 6 7 8 9 10 11
| @Component public class A { @Autowired private B b; }
@Component public class B { @Autowired private A a; }
|
- 创建 Bean A → 创建 Bean B(因为 A 依赖 B)
- 创建 Bean B → 发现依赖 A,此时 A 尚未初始化完成
- Spring 提前将 A 的“半成品引用”暴露(放入二级缓存)
- B 通过二级缓存拿到 A 的早期引用,继续完成自己的初始化
- 最终 A 和 B 都完成初始化,循环依赖解决
spring不能解决的情况
构造器注入的循环依赖,这时 A 和 B 都需要对方完成构造才能继续,没有一个能“先放个半成品”,所以无法解决。
1 2 3 4 5 6 7 8 9 10 11
| @Component public class A { private final B b; public A(B b) { this.b = b; } }
@Component public class B { private final A a; public B(A a) { this.a = a; } }
|
怎么解决这个问题呢?
- 改成字段注入 / Setter 注入,即采用spring的 @Autowired,交给spring去管理,spring利用三级缓存机制去解决。
- 使用 @Lazy 延迟注入,@Lazy 让 Spring 注入的是一个代理对象,延迟真正实例化,从而打破循环依赖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Component public class A { private final B b; public A(@Lazy B b) { this.b = b; } }
@Component public class B { private final A a; public B(@Lazy A a) { this.a = a; } }
|
- 重构你的代码,从设计层面去解决。