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 的创建过程能够顺利进行。

相关推荐
apocelipes8 分钟前
atomic不是免费午餐
java·性能优化·golang·并发
A了LONE19 分钟前
cv弹窗,退款确认弹窗
java·服务器·前端
蔡楚门39 分钟前
福彩双色球第2025088期篮球号码分析
java
慕y2741 小时前
Java学习第九十六部分——Eureka
java·学习·eureka
头发那是一根不剩了1 小时前
信创应用服务器TongWeb安装教程、前后端分离应用部署全流程
java·信创·tongweb
我科绝伦(Huanhuan Zhou)2 小时前
【故障案例】Redis缓存三大难题:雪崩、击穿、穿透解析与实战解决方案
redis·缓存·mybatis
22:30Plane-Moon2 小时前
Servlet作用域,监听器,JSP九大内置对象
java·开发语言·servlet
小白(猿)员2 小时前
JVM、JDK、JRE的区别
java·开发语言·jvm
axban2 小时前
QT中删除控件的注意事项、deleteLater和delete的区别
java·数据库·qt
MicoZone3 小时前
JDK源码
java