spring三级缓存如何解决循环依赖问题
Spring 的三级缓存机制是解决循环依赖问题的核心手段之一。通过三级缓存,Spring 能够在 Bean 的创建过程中提前暴露未完全初始化的 Bean 实例,从而解决单例 Bean 的循环依赖问题。
什么是循环依赖?
循环依赖是指两个或多个 Bean 在初始化时相互依赖。例如:
@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;}
}
在这种情况下,A
依赖 B
,而 B
又依赖 A
,导致 Spring 在创建 A
和 B
时陷入死循环。
Spring 的三级缓存
Spring 使用三级缓存来解决循环依赖问题,这三级缓存分别是:
-
一级缓存(singletonObjects):
- 存储已经完全初始化完成的单例 Bean。
- 当一个 Bean 完全初始化后,它会被放入一级缓存中。
-
二级缓存(earlySingletonObjects):
- 存储提前暴露的、尚未完全初始化的 Bean 实例。
- 如果某个 Bean 还未完成初始化,但已经被其他 Bean 依赖,Spring 会将它的实例放入二级缓存中。
-
三级缓存(singletonFactories):
- 存储用于创建早期 Bean 实例的工厂对象(
ObjectFactory
)。 - 当需要提前暴露一个 Bean 实例时,Spring 会使用三级缓存中的工厂对象来创建该 Bean 的早期引用。
- 存储用于创建早期 Bean 实例的工厂对象(
三级缓存的工作原理
以下是 Spring 如何利用三级缓存解决循环依赖问题的详细流程:
1. 创建 Bean A
- Spring 开始创建
A
的实例。 - 在
A
的构造器中,发现需要注入B
,因此暂停A
的初始化,转而开始创建B
。
2. 创建 Bean B
- Spring 开始创建
B
的实例。 - 在
B
的构造器中,发现需要注入A
。 - 此时,
A
还未完全初始化,但 Spring 已经为A
创建了一个实例(未完成初始化)。
3. 提前暴露 Bean A
- Spring 将
A
的实例放入三级缓存(singletonFactories
)中。 - 如果其他 Bean 需要依赖
A
,Spring 会从三级缓存中获取A
的工厂对象,并调用工厂方法生成A
的早期引用(未完全初始化的实例)。 - 生成的早期引用会被放入二级缓存(
earlySingletonObjects
),并从三级缓存中移除。
4. 注入 Bean A 到 Bean B
B
获取到A
的早期引用,并将其注入到自己的属性中。B
完成初始化后,被放入一级缓存(singletonObjects
)中。
5. 完成 Bean A 的初始化
- 回到
A
的初始化过程,此时B
已经完全初始化。 A
获取到B
的完全初始化实例,并完成自己的初始化。- 最终,
A
被放入一级缓存中。
三级缓存的关键点
-
为什么需要三级缓存?
- 如果只有二级缓存(直接存储早期引用),则无法支持某些复杂的场景,比如代理对象的创建。
- 三级缓存允许 Spring 在需要时动态生成代理对象,而不是提前创建代理对象。
-
代理对象的处理
- 如果
A
或B
是被代理的对象(如使用了@Transactional
或 AOP),Spring 会在三级缓存中通过工厂对象生成代理对象,而不是直接暴露原始实例。 - 这确保了即使存在循环依赖,最终注入的仍然是正确的代理对象。
- 如果
-
只支持单例 Bean
- Spring 的三级缓存机制仅适用于单例作用域的 Bean。对于原型(Prototype)作用域的 Bean,Spring 不会缓存其实例,因此无法解决循环依赖问题。
示例代码分析
以下是一个简单的循环依赖示例,展示了 Spring 如何通过三级缓存解决问题:
@Component
public class A {private final B b;@Autowiredpublic A(B b) {System.out.println("Initializing A");this.b = b;}
}@Component
public class B {private final A a;@Autowiredpublic B(A a) {System.out.println("Initializing B");this.a = a;}
}
执行流程
- Spring 开始创建
A
。 - 发现
A
依赖B
,暂停A
的初始化,开始创建B
。 - 发现
B
依赖A
,将A
的早期引用放入三级缓存。 B
获取到A
的早期引用,完成初始化。- 回到
A
的初始化过程,A
获取到完全初始化的B
,完成初始化。
输出结果
Initializing A
Initializing B
总结
Spring 的三级缓存机制通过以下步骤解决了循环依赖问题:
- 三级缓存(singletonFactories):存储工厂对象,用于动态生成早期引用。
- 二级缓存(earlySingletonObjects):存储提前暴露的早期引用。
- 一级缓存(singletonObjects):存储完全初始化的 Bean 实例。
这种机制确保了即使在存在循环依赖的情况下,Spring 仍然能够正确地完成 Bean 的初始化和依赖注入。然而需要注意的是,这种机制仅适用于单例 Bean,且不支持复杂的代理场景(如多层代理)。