在 Spring 中,循环依赖是指两个或多个 bean 之间相互依赖,形成一个循环。Spring 主要通过三级缓存来解决循环依赖问题。
一、三级缓存的概念
-
一级缓存(singletonObjects):
- 存放完全初始化好的单例 bean 对象。
- 当一个 bean 被完全创建并初始化后,会被放入这个缓存中供后续使用。
-
二级缓存(earlySingletonObjects):
- 存放早期曝光的单例 bean 对象。
- 这些对象还没有完全初始化完成,但已经可以提前暴露出去,以解决循环依赖问题。
-
三级缓存(singletonFactories):
- 存放 ObjectFactory 对象,用于在需要时创建单例 bean。
- 这个工厂对象可以在创建 bean 的过程中,根据需要生成 bean 的实例。
二、解决循环依赖的过程
- 假设存在两个 bean A 和 B,A 依赖 B,B 也依赖 A。
- 当创建 bean A 时,首先会从一级缓存中查找是否已经存在 bean A,如果不存在则继续创建过程。
- 创建 bean A 的过程中,发现需要 bean B,于是开始创建 bean B。
- 同样,在创建 bean B 时,先从一级缓存查找 bean B 是否存在,不存在则继续。
- 创建 bean B 的过程中,发现需要 bean A,此时会去一级缓存查找,发现也不存在。
- 由于一级缓存中没有找到 bean A,接着会去二级缓存查找,也没有找到。
- 然后去三级缓存查找,此时三级缓存中也没有 bean A 的实例,但是有一个 ObjectFactory 对象,这个对象可以用来创建 bean A 的实例。
- Spring 会调用这个 ObjectFactory 对象来创建一个 bean A 的实例,并将这个实例放入二级缓存中,同时继续创建 bean B。
- bean B 创建完成后,将其放入一级缓存。
- 此时,bean A 的创建过程继续,由于 bean B 已经存在于一级缓存中,所以可以完成 bean A 的创建,并将其放入一级缓存。
通过这种方式,Spring 利用三级缓存有效地解决了循环依赖问题。在创建 bean 的过程中,通过提前曝光未完全初始化的 bean 实例,使得在循环依赖的情况下,其他 bean 可以获取到正在创建中的 bean,从而保证了创建过程的顺利进行。
总结:
在 Spring 的三级缓存中,singletonFactories
即存放着ObjectFactory
对象的三级缓存起着至关重要的作用。
singletonFactories
的工作过程如下:
一、创建阶段
当 Spring 容器开始创建一个 bean 时,首先会检查一级缓存(singletonObjects
)和二级缓存(earlySingletonObjects
)中是否已经存在该 bean。如果都不存在,就会进入 bean 的创建流程。在这个过程中,如果发现该 bean 存在循环依赖的情况,Spring 会将一个用于创建该 bean 的ObjectFactory
对象放入singletonFactories
中。这个ObjectFactory
实际上是一个匿名内部类,它持有对正在创建的 bean 的引用,并在被调用时返回这个正在创建的 bean 的实例。
例如,当创建 bean A 且发现其依赖 bean B,而创建 bean B 时又发现依赖 bean A,此时对于 bean A,就会在singletonFactories
中放入一个能创建 bean A 的ObjectFactory
。
二、解决循环依赖阶段
当创建 bean B 的过程中需要 bean A 时,首先会在一级缓存中查找,未找到;接着在二级缓存中查找,也未找到;然后在三级缓存中查找,此时会找到那个用于创建 bean A 的ObjectFactory
对象。Spring 会调用这个ObjectFactory
对象来获取一个 bean A 的实例,这个实例虽然还没有完全初始化完成(因为 bean A 的创建过程还在继续),但已经可以提供给 bean B 使用了。这个实例会被放入二级缓存中,以便后续 bean A 的创建过程可以继续使用它来完成自身的初始化。
三、最终完成阶段
当 bean A 的创建过程全部完成后,会将完全初始化好的 bean A 放入一级缓存中,并从二级缓存和三级缓存中移除相关的引用,以保证缓存的一致性和准确性。
总之,singletonFactories
通过在适当的时候提供一个可以创建 bean 的工厂对象,使得 Spring 在处理循环依赖问题时能够及时获取到正在创建中的 bean 的实例,从而保证了 bean 的创建过程能够顺利进行。