文章目录
- 什么是Spring的循环依赖?
- Setter 方法循环依赖
- 三级缓存
- Spring 是如何利用三级缓存解决 Bean 的循环依赖?
- 有了三级缓存为什么还需要二级缓存?
- 构造器循环依赖
- 总结
什么是Spring的循环依赖?
在 Spring 框架中,循环依赖
是指两个或多个 Bean 相互依赖,形成一个闭环。
- Setter 方法循环依赖:Bean A 通过 setter 方法注入 Bean B,Bean B 又通过 setter 方法注入 Bean A。(即@Autowired)
- 构造器循环依赖:Bean A 需要 Bean B 的实例,而 Bean B 又需要 Bean A 的实例。
Setter 方法循环依赖
三级缓存
三级缓存是 Spring 处理 Setter 循环依赖的关键机制
- Singleton Objects(一级缓存):存放已完全初始化的单例 Bean。
- Early Singleton Objects(二级缓存):缓存早期的 Bean 对象(就是这些 Bean 已经被实例化,但是还没有被完全初始化)。也可避免重复通过工厂获取早期引用。
- Singleton Factories(三级缓存):存储可以创建单例Bean实例的工厂,可以创
建代理对象(二级缓存不能解决的)
,普通对象
Spring 是如何利用三级缓存解决 Bean 的循环依赖?
三级缓存是 Spring 框架中用于解决 Bean 循环依赖的一种机制。
一级缓存:
二级缓存:
三级缓存:
二级缓存用于存储已经创建但未完全初始化的早期 Bean 引用。但是在 Bean 被完全初始化之前,早期引用可能需要通过代理对象
(如 AOP 代理)创建。三级缓存正是用于存储这种生成早期引用的工厂方法。
通过三级缓存,Spring 可以在 ObjectFactory 中定义如何创建早期引用(如处理代理
),这样就可以灵活地在需要时(特别是在循环依赖情况下)提供一个代理后的早期引用给其他 Bean 使用。
有了三级缓存为什么还需要二级缓存?
二级缓存的引入可以防止多次使用对象工厂(ObjectFactory)创建多个对象实例
,从而避免不必要的多例问题
首次访问早期引用: 当一个 Bean 正在初始化过程中(比如 A 依赖 B ,但 B 尚未完全初始化)时,Spring 会首先检查一级缓存(完全初始化的单例对象)中是否有 B。如果没有,再检查二级缓存(早期暴露的对象引用)。如果二级缓存中也没有,再通过三级缓存的 ObjectFactory
获取 B 的早期引用。 一旦通过 ObjectFactory 获取了 B 的早期引用,Spring 会将这个引用放入二级缓存。
这样,后续对 B 的早期引用访问就不会再触发 ObjectFactory,而是直接从二级缓存中获取。
构造器循环依赖
三级缓存机制可以自动处理基于单例作用域的 setter 方法循环依赖,但无法解决由构造器注入引起的循环依赖。
比如:
@Component
public class A {
private B b;
@Autowired
public A(B b) {
this.b = b;
}
}
@Component
public class B {
private A a;
@Autowired
public B(A a) {
this.a = a;
}
}
解决方法:使用 @Lazy
注解,在构造器注入中使用 @Lazy 注解,可以推迟依赖的初始化,从而打破循环。@Lazy 注解表示在实际使用时才创建实例,而不是在启动时。
@Component
public class A {
private B b;
@Autowired
// 添加注解 @Lazy
public A(@Lazy B b) {
this.b = b;
}
}
@Component
public class B {
private A a;
@Autowired
public B(@Lazy A a) {
this.a = a;
}
}
总结
-
Setter 注入循环依赖:Spring 通过三级缓存机制自动处理
-
构造器注入循环依赖:Spring 无法自动解决,使用 @Lazy 注解
❤觉得有用的可以留个关注丫~❤