详细介绍下spring三级缓存以及如何解决循环依赖

Spring 的三级缓存机制是 Spring 容器用来解决循环依赖问题的关键技术。循环依赖是 Spring 容器在初始化 Bean 时可能遇到的一个问题,尤其是在使用构造函数注入时。通过三级缓存机制,Spring 能够优雅地解决这一问题。
Spring 三级缓存机制
Spring 的三级缓存机制主要包括以下三个缓存结构:
-
一级缓存(singletonObjects)
- 这是一个
Map,用于存储完全初始化完成的单例 Bean。这些 Bean 已经完成了所有的依赖注入和初始化操作,可以直接使用。
- 这是一个
-
二级缓存(earlySingletonObjects)
- 这是一个
Map,用于存储尚未完全初始化的单例 Bean。这些 Bean 已经被实例化并进行了依赖注入,但尚未执行初始化方法(@PostConstruct或init-method)。这些 Bean 可以用于解决循环依赖,因为它们已经具有足够的状态来参与依赖注入。
- 这是一个
-
三级缓存(singletonFactories)
- 这是一个
Map,用于存储 Bean 的ObjectFactory。当一个 Bean 被请求时,Spring 会先从一级缓存中查找。如果找不到,则尝试从二级缓存中查找。如果仍然找不到,则从三级缓存中获取ObjectFactory,并使用它来创建 Bean。创建完成后,这个 Bean 会被放入二级缓存中,等待完全初始化完成后再放入一级缓存。
- 这是一个
如何解决循环依赖
Spring 使用三级缓存机制来解决循环依赖问题。以下是具体步骤:
-
创建 Bean 实例
- 当一个 Bean 被请求时,Spring 首先检查一级缓存中是否存在该 Bean。如果不存在,Spring 会创建一个
ObjectFactory,并将其放入三级缓存中。
- 当一个 Bean 被请求时,Spring 首先检查一级缓存中是否存在该 Bean。如果不存在,Spring 会创建一个
-
实例化 Bean
- Spring 使用
ObjectFactory创建 Bean 的实例,并将这个实例放入二级缓存中。这个 Bean 已经被实例化并进行了依赖注入,但尚未执行初始化方法。
- Spring 使用
-
依赖注入
- 如果在这个过程中需要注入其他 Bean,Spring 会检查二级缓存中是否存在该 Bean。如果存在,就可以直接使用,从而避免了循环依赖问题。
-
** Bean 初始化**
- 依赖注入完成后,Spring 会执行初始化方法(如
@PostConstruct或init-method)。初始化完成后,将 Bean 从二级缓存转移到一级缓存中。
- 依赖注入完成后,Spring 会执行初始化方法(如
实际场景及代码示例
假设我们有两个服务:ServiceA 和 ServiceB。ServiceA 依赖于 ServiceB,而 ServiceB 也依赖于 ServiceA。这种相互依赖形成了循环依赖。
定义 ServiceA 和 ServiceB
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ServiceA {
private final ServiceB serviceB;
@Autowired
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
public void doSomething() {
System.out.println("ServiceA is doing something...");
serviceB.doSomething();
}
}
@Service
public class ServiceB {
private final ServiceA serviceA;
@Autowired
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
public void doSomething() {
System.out.println("ServiceB is doing something...");
serviceA.doSomething();
}
}
Spring 如何通过三级缓存解决循环依赖
-
创建
ServiceA的实例- Spring 检查一级缓存中是否存在
ServiceA,发现不存在。 - Spring 创建
ServiceA的ObjectFactory,并将其放入三级缓存中。
- Spring 检查一级缓存中是否存在
-
实例化
ServiceA- Spring 使用
ObjectFactory创建ServiceA的实例,并将这个实例放入二级缓存中。 - 在创建
ServiceA的实例时,需要注入ServiceB。
- Spring 使用
-
创建
ServiceB的实例- Spring 检查一级缓存中是否存在
ServiceB,发现不存在。 - Spring 创建
ServiceB的ObjectFactory,并将其放入三级缓存中。
- Spring 检查一级缓存中是否存在
-
实例化
ServiceB- Spring 使用
ObjectFactory创建ServiceB的实例,并将这个实例放入二级缓存中。 - 在创建
ServiceB的实例时,需要注入ServiceA。 - Spring 检查二级缓存中是否存在
ServiceA,发现存在,直接使用二级缓存中的ServiceA。
- Spring 使用
-
依赖注入完成
ServiceB的实例创建完成,并依赖注入了ServiceA。- Spring 将
ServiceB的实例从二级缓存转移到一级缓存中。
-
回过头来处理
ServiceAServiceA的实例已经创建完成,并依赖注入了ServiceB。- Spring 将
ServiceA的实例从二级缓存转移到一级缓存中。
注意事项
- 构造函数注入的限制:Spring 仅在使用构造函数注入时支持解决循环依赖问题。如果使用
@Lazy注解,可以延迟单个 Bean 的初始化,从而避免循环依赖。 - setter 方法注入的优势:如果使用 setter 方法注入,Spring 会在 Bean 完全初始化之前注入依赖,从而避免循环依赖问题。
总结
Spring 的三级缓存机制通过以下步骤解决循环依赖问题:
- 一级缓存存储完全初始化完成的 Bean。
- 二级缓存存储尚未完全初始化的 Bean。
- 三级缓存存储 Bean 的
ObjectFactory。
通过这种方式,Spring 可以在 Bean 的依赖注入过程中优雅地解决循环依赖问题。
1201

被折叠的 条评论
为什么被折叠?



