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 小时前
分布式缓存 + 数据存储 + 消息队列知识体系
分布式·缓存
Dlwyz3 小时前
redis-击穿、穿透、雪崩
数据库·redis·缓存
Theodore_10224 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
冰帝海岸5 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象5 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了6 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
小二·6 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic6 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
懒洋洋大魔王6 小时前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
武子康6 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud