Spring如何通过三级缓存解决循环依赖的问题

在 Spring 中,循环依赖是指两个或多个 bean 之间相互依赖,形成一个循环。Spring 主要通过三级缓存来解决循环依赖问题。

一、三级缓存的概念

  1. 一级缓存(singletonObjects):

    • 存放完全初始化好的单例 bean 对象。
    • 当一个 bean 被完全创建并初始化后,会被放入这个缓存中供后续使用。
  2. 二级缓存(earlySingletonObjects):

    • 存放早期曝光的单例 bean 对象。
    • 这些对象还没有完全初始化完成,但已经可以提前暴露出去,以解决循环依赖问题。
  3. 三级缓存(singletonFactories):

    • 存放 ObjectFactory 对象,用于在需要时创建单例 bean。
    • 这个工厂对象可以在创建 bean 的过程中,根据需要生成 bean 的实例。

二、解决循环依赖的过程

  1. 假设存在两个 bean A 和 B,A 依赖 B,B 也依赖 A。
    • 当创建 bean A 时,首先会从一级缓存中查找是否已经存在 bean A,如果不存在则继续创建过程。
  2. 创建 bean A 的过程中,发现需要 bean B,于是开始创建 bean B。
    • 同样,在创建 bean B 时,先从一级缓存查找 bean B 是否存在,不存在则继续。
    • 创建 bean B 的过程中,发现需要 bean A,此时会去一级缓存查找,发现也不存在。
  3. 由于一级缓存中没有找到 bean A,接着会去二级缓存查找,也没有找到。
    • 然后去三级缓存查找,此时三级缓存中也没有 bean A 的实例,但是有一个 ObjectFactory 对象,这个对象可以用来创建 bean A 的实例。
  4. Spring 会调用这个 ObjectFactory 对象来创建一个 bean A 的实例,并将这个实例放入二级缓存中,同时继续创建 bean B。
    • bean B 创建完成后,将其放入一级缓存。
  5. 此时,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 的创建过程能够顺利进行。

相关推荐
神仙别闹12 分钟前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭36 分钟前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫1 小时前
泛型(2)
java
超爱吃士力架1 小时前
邀请逻辑
java·linux·后端
南宫生1 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石1 小时前
12/21java基础
java
李小白661 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp2 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
Code apprenticeship2 小时前
怎么利用Redis实现延时队列?
数据库·redis·缓存
装不满的克莱因瓶2 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb