1.Spring三级缓存
Spring 的三级缓存机制主要应用于其依赖注入(DI)容器中,特别是在单例(Singleton)作用域的 Bean 创建过程中。这三级缓存的设计目的是为了提高性能,减少重复创建 Bean 的开销,并确保 Bean 的唯一性和一致性。
1.1 一级缓存(Singleton Objects Cache)
存储位置:singletonObjects,这是一个 ConcurrentHashMap,用于存储已经完全初始化的单例 Bean。
作用:当一个 Bean 完全初始化并准备好使用时,它会被放入 singletonObjects 中。下次请求同一个 Bean 时,直接从这个缓存中获取,无需重新创建。
特点:存储的是完全初始化的 Bean 实例。
1.2 二级缓存(Early Singleton Objects Cache)
存储位置:earlySingletonObjects,这也是一个 ConcurrentHashMap,用于存储尚未完全初始化的单例 Bean。
作用:在 Bean 的初始化过程中,如果其他 Bean 依赖于当前正在初始化的 Bean,可以从 earlySingletonObjects 中获取一个早期的引用。这样可以解决循环依赖的问题。
特点:存储的是未完全初始化的 Bean 实例。
1.3 三级缓存(Singleton Factories Cache)
存储位置:singletonFactories,同样是一个 ConcurrentHashMap,用于存储 Bean 工厂对象。
作用:在 Bean 初始化过程中,如果其他 Bean 依赖于当前正在初始化的 Bean,可以从 singletonFactories 中获取一个工厂对象,通过工厂对象创建一个早期的 Bean 实例。
特点:存储的是 Bean 工厂对象,用于创建早期的 Bean 实例。
2.三级缓存的工作机制
2.1 检查一级缓存
当请求一个单例 Bean 时,首先检查一级缓存(singletonObjects)。
如果找到,直接返回该 Bean 实例。
2.2 检查二级缓存
如果一级缓存(singletonObjects)中没有找到,检查二级缓存(earlySingletonObjects)。
如果找到,返回该 Bean 实例(未完全初始化的早期引用)。
2.3 检查三级缓存
如果二级缓存(earlySingletonObjects) 中也没有找到,检查三级缓存(singletonFactories)。这是存放 Bean 工厂方法的地方。当容器开始创建 Bean 时,三级缓存存储了生成 Bean 的工厂方法。如果在三级缓存中找到,则使用工厂对象创建一个早期的 Bean 实例,并将其放入二级缓存( earlySingletonObjects ),然后返回该早期引用。
2.4 创建并初始化 Bean
如果以上缓存中也没有找到,开始创建并初始化 Bean。Spring 将创建 Bean。这个过程中,Bean 会被放入三级缓存,并调用 populateBean 方法注入依赖,最后将完成初始化的 Bean 放入一级缓存中。在初始化过程中,如果其他 Bean 依赖于当前正在初始化的 Bean,可以通过二级缓存(earlySingletonObjects)或三级缓存(singletonFactories) 获取早期引用。
初始化完成后,将 Bean 实例放入一级缓存(singletonObjects)中,并从二级缓存(earlySingletonObjects) 和三级缓存(singletonFactories)缓存中移除相关的条目。
3.循环依赖
循环依赖(Circular Dependency)是指两个或多个对象相互依赖,导致它们无法被正常创建。最常见的场景是两个类互相引用对方,构成了一个闭环。
例如,假设有两个类 A 和 B:
@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;
}
}
在上面的代码中,A 类依赖于 B 类,B 类又依赖于 A 类,这种循环依赖关系会导致Spring容器无法解决对象的创建。
Spring默认不允许有循环依赖存在,因为这会造成无限的递归调用,进而导致堆栈溢出错误。
4.三级缓存如何解决循环依赖
假设我们有两个 Bean:A 和 B,它们之间存在循环依赖关系,即 A 依赖 B,B 依赖 A。
4.1 请求 Bean A
- Spring 容器接收到请求,开始创建 Bean A。
- 检查一级缓存,如果没有找到 Bean A,继续检查二级缓存和三级缓存。
- 如果都没有找到,开始实例化 Bean A,并将 Bean A 的工厂对象放入三级缓存中。
4.2 初始化 Bean A
- 在初始化 Bean A 的过程中,发现 Bean A 依赖于 Bean B。
- 请求 Bean B,开始创建 Bean B。
- 检查一级缓存,如果没有找到 Bean B,继续检查二级缓存和三级缓存。
- 如果都没有找到,开始实例化 Bean B,并将 Bean B 的工厂对象放入三级缓存中。
4.2 初始化 Bean B
- 在初始化 Bean B 的过程中,发现 Bean B 依赖于 Bean A。
- 请求 Bean A,此时 Bean A 还没有完全初始化,但是已经在三级缓存中。
- 从三级缓存中获取 Bean A 的工厂对象,创建一个早期的 Bean A 实例,并将这个早期实例放入二级缓存中。
- 将这个早期的 Bean A 实例注入到 Bean B 中,继续初始化 Bean B。
4.2 完成 Bean B 的初始化
- Bean B 完成初始化后,将 Bean B 放入一级缓存中,并从二级缓存和三级缓存中移除相关的条目。
4.2 完成 Bean A 的初始化
- 回到 Bean A 的初始化过程,继续初始化 Bean A。
- 完成 Bean A 的初始化后,将 Bean A 放入一级缓存中,并从二级缓存和三级缓存中移除相关的条目。