当前位置: 首页 > news >正文

深度解析 Spring Boot 循环依赖:原理、源码与解决方案

在 Spring Boot 开发中,循环依赖是一个常见且容易被忽视的技术点。当两个或多个 Bean 相互引用时,就会形成循环依赖(如 A 依赖 B,B 依赖 A)。初学者往往会困惑:Spring 为什么能自动处理这种看似矛盾的依赖关系?本文将从原理、源码实现到解决方案,全方位剖析 Spring Boot 的循环依赖机制。

一、什么是循环依赖?

循环依赖指的是两个或多个 Bean 之间存在相互引用的关系,形成闭环。例如:

// ServiceA依赖ServiceB
@Service
public class ServiceA {@Autowiredprivate ServiceB serviceB;
}// ServiceB依赖ServiceA
@Service
public class ServiceB {@Autowiredprivate ServiceA serviceA;
}

上述代码中,ServiceA需要注入ServiceB,而ServiceB同时需要注入ServiceA,形成了最简单的循环依赖。在传统的对象创建过程中,这种相互依赖会导致 "先有鸡还是先有蛋" 的悖论,但 Spring 通过特殊的机制解决了大部分场景下的循环依赖问题。

二、Spring Boot 如何处理循环依赖?

Spring 容器对循环依赖的处理能力,是其 Bean 管理机制的重要体现。核心解决方案可概括为:三级缓存 + 提前暴露半成品 Bean

1. 三级缓存的核心作用

Spring 通过三个缓存(实际上是三个 Map)来协调 Bean 的创建与依赖注入过程,这三个缓存定义在DefaultSingletonBeanRegistry中:

// 一级缓存:存储完全初始化完成的Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);// 二级缓存:存储半成品Bean(已实例化但未完成属性注入和初始化)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);// 三级缓存:存储Bean的工厂对象,用于生成半成品Bean的代理对象
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  • 一级缓存(singletonObjects):存放已完成所有初始化步骤的 Bean,是最终可被使用的成品 Bean。
  • 二级缓存(earlySingletonObjects):存放已实例化但尚未完成属性注入和初始化的半成品 Bean,用于解决循环依赖时的临时引用。
  • 三级缓存(singletonFactories):存放ObjectFactory对象,该工厂用于在需要时生成 Bean 的早期引用(可能是原始对象或代理对象)。

2. 循环依赖的解决流程

以ServiceA和ServiceB的循环依赖为例,Spring 的处理流程如下:

  1. 创建 ServiceA
    1. 调用getBean(ServiceA),检查一级缓存,未命中。
    2. 实例化ServiceA(通过构造方法创建对象,但未注入属性)。
    3. 将ServiceA的ObjectFactory放入三级缓存(singletonFactories)。
    4. 开始为ServiceA注入属性,发现依赖ServiceB。
  2. 创建 ServiceB
    1. 调用getBean(ServiceB),检查一级缓存,未命中。
    2. 实例化ServiceB,将其ObjectFactory放入三级缓存。</
http://www.dtcms.com/a/325024.html

相关文章:

  • PhotoDirector 安卓版:功能强大的照片编辑与美化应用
  • TypeScript中的type和interface的区别是什么?
  • Shell脚本-数组定义
  • OpenEnler等Linux系统中安装git工具的方法
  • DDR中的POD与ODT
  • 分布式事务原理(高并发系统下的数据一致性保障)
  • 一、线性规划
  • 免费数字人API开发方案
  • 高精度计算+快速幂算法
  • 【算法题】:斐波那契数列
  • 【langchain】如何给langchain提issue和提pull request?
  • SpringIoc 实践和应用--XML配置
  • 数据结构-deque(双端队列)和queue(队列)区别
  • Flask多进程数据库访问问题详解
  • spring全家桶使用教程
  • Lua语言元表、协同程序
  • 密码学的数学基础2-Paillier为什么产生密钥对比RSA慢
  • SQL三剑客:DELETE、TRUNCATE、DROP全解析
  • 深度相机---双目深度相机
  • 浏览器CEFSharp+X86+win7 之 浏览器右键菜单(六)
  • Mysql笔记-存储过程与存储函数
  • vulnhub-doubletrouble靶场攻略
  • Linux C文件操作函数
  • 谷歌DeepMind发布Genie 3:通用型世界模型,可生成前所未有多样化的交互式虚拟环境
  • C++移动语义、完美转发及编译器优化零拷贝
  • PostgreSQL 批量COPY导入优化参数配置
  • 近红外与可见光图像融合的多种方法实现
  • OpenAI正式发布GPT-5:迈向AGI的关键一步
  • Java基础-多线程
  • lesson34:深入理解Python线程:从基础到实战优化