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

相关推荐
码路飞2 小时前
GPT-5.3 Instant 终于学会好好说话了,顺手对比了下同天发布的 Gemini 3.1 Flash-Lite
java·javascript
SimonKing2 小时前
OpenCode AI编程助手如何添加Skills,优化项目!
java·后端·程序员
Seven974 小时前
剑指offer-80、⼆叉树中和为某⼀值的路径(二)
java
怒放吧德德15 小时前
Netty 4.2 入门指南:从概念到第一个程序
java·后端·netty
雨中飘荡的记忆17 小时前
大流量下库存扣减的数据库瓶颈:Redis分片缓存解决方案
java·redis·后端
心之语歌19 小时前
基于注解+拦截器的API动态路由实现方案
java·后端
华仔啊20 小时前
Stream 代码越写越难看?JDFrame 让 Java 逻辑回归优雅
java·后端
ray_liang20 小时前
用六边形架构与整洁架构对比是伪命题?
java·架构
用户83071968408220 小时前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Ray Liang1 天前
用六边形架构与整洁架构对比是伪命题?
java·python·c#·架构设计