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

Spring 是如何解决循环依赖问题

Spring 框架通过 三级缓存 机制来解决循环依赖问题。循环依赖是指两个或多个 Bean 相互依赖,形成一个闭环,例如 Bean A 依赖 Bean B,而 Bean B 又依赖 Bean A。Spring 通过提前暴露未完全初始化的 Bean 来解决这个问题。

以下是 Spring 解决循环依赖的详细机制和流程:


1. Spring Bean 的生命周期

在理解循环依赖之前,需要了解 Spring Bean 的生命周期。Spring Bean 的创建过程主要包括以下步骤:

  1. 实例化:通过构造函数或工厂方法创建 Bean 的实例。
  2. 属性填充:将依赖的 Bean 注入到当前 Bean 中(通过 @Autowired@Resource 等注解或 XML 配置)。
  3. 初始化:调用初始化方法(如 @PostConstructInitializingBeanafterPropertiesSet 方法)。
  4. 销毁:在容器关闭时调用销毁方法。

循环依赖通常发生在 属性填充 阶段。


2. 三级缓存机制

Spring 通过三级缓存来解决循环依赖问题。三级缓存分别是:

  1. 一级缓存(Singleton Objects):存储完全初始化好的单例 Bean。
  2. 二级缓存(Early Singleton Objects):存储提前暴露的未完全初始化的 Bean(仅实例化,未填充属性)。
  3. 三级缓存(Singleton Factories):存储 Bean 的工厂对象(ObjectFactory),用于生成提前暴露的 Bean。

3. 解决循环依赖的流程

以下是一个典型的循环依赖场景:

  • Bean A 依赖 Bean B。
  • Bean B 依赖 Bean A。

Spring 解决循环依赖的流程如下:

步骤 1:创建 Bean A
  1. Spring 开始创建 Bean A,调用构造函数实例化 Bean A。
  2. 将 Bean A 的工厂对象(ObjectFactory)放入三级缓存。
  3. 开始填充 Bean A 的属性,发现 Bean A 依赖 Bean B。
步骤 2:创建 Bean B
  1. Spring 开始创建 Bean B,调用构造函数实例化 Bean B。
  2. 将 Bean B 的工厂对象(ObjectFactory)放入三级缓存。
  3. 开始填充 Bean B 的属性,发现 Bean B 依赖 Bean A。
步骤 3:解决 Bean B 的依赖
  1. Spring 从三级缓存中获取 Bean A 的工厂对象,生成 Bean A 的早期引用(未完全初始化的 Bean A)。
  2. 将 Bean A 的早期引用放入二级缓存,并从三级缓存中移除 Bean A 的工厂对象。
  3. 将 Bean A 的早期引用注入到 Bean B 中。
  4. Bean B 完成属性填充和初始化,成为一个完全初始化的 Bean。
  5. 将 Bean B 放入一级缓存。
步骤 4:完成 Bean A 的创建
  1. Spring 将 Bean B 注入到 Bean A 中。
  2. Bean A 完成属性填充和初始化,成为一个完全初始化的 Bean。
  3. 将 Bean A 放入一级缓存,并从二级缓存中移除 Bean A 的早期引用。

4. 代码示例

以下是一个简单的循环依赖示例:

@Component
public class BeanA {@Autowiredprivate BeanB beanB;
}@Component
public class BeanB {@Autowiredprivate BeanA beanA;
}

Spring 会通过三级缓存机制解决 BeanABeanB 之间的循环依赖。


5. 解决循环依赖的限制

Spring 的循环依赖解决方案有以下限制:

  1. 仅支持单例 Bean:Spring 只能解决单例作用域(Singleton)的 Bean 的循环依赖。原型作用域(Prototype)的 Bean 无法解决循环依赖。

  2. 构造函数注入无法解决循环依赖:如果循环依赖是通过构造函数注入的,Spring 无法解决。因为构造函数注入需要在实例化时完成依赖注入,而三级缓存机制无法提前暴露未完全初始化的 Bean。

    @Component
    public class BeanA {private final BeanB beanB;@Autowiredpublic BeanA(BeanB beanB) {this.beanB = beanB;}
    }@Component
    public class BeanB {private final BeanA beanA;@Autowiredpublic BeanB(BeanA beanA) {this.beanA = beanA;}
    }
    

    上述代码会抛出 BeanCurrentlyInCreationException 异常。

  3. 避免复杂的循环依赖:虽然 Spring 可以解决简单的循环依赖,但复杂的循环依赖可能会导致代码难以维护和理解,应尽量避免。


6. 如何避免循环依赖

  1. 使用 Setter 注入或字段注入:避免使用构造函数注入。
  2. 重新设计代码:通过重新设计类之间的关系,消除循环依赖。
  3. 使用 @Lazy 注解:延迟加载依赖的 Bean。
    @Component
    public class BeanA {private final BeanB beanB;@Autowiredpublic BeanA(@Lazy BeanB beanB) {this.beanB = beanB;}
    }
    

7. 缓存状态的变化

以下是缓存状态的变化过程:

步骤一级缓存 (singletonObjects)二级缓存 (earlySingletonObjects)三级缓存 (singletonFactories)
创建 BeanA 实例后BeanA 的工厂对象
创建 BeanB 实例后BeanABeanB 的工厂对象
解决 BeanB 依赖后BeanA 的早期引用BeanB 的工厂对象
BeanB 创建完成后BeanBBeanA 的早期引用
BeanA 创建完成后BeanABeanB

总结

Spring 通过三级缓存机制解决了单例 Bean 的循环依赖问题,但构造函数注入和原型 Bean 的循环依赖无法解决。在实际开发中,应尽量避免循环依赖,保持代码的清晰和可维护性。如果无法避免,可以使用 Setter 注入或 @Lazy 注解来解决。

相关文章:

Spring 是如何解决循环依赖问题

Spring 框架通过 三级缓存 机制来解决循环依赖问题。循环依赖是指两个或多个 Bean 相互依赖,形成一个闭环,例如 Bean A 依赖 Bean B,而 Bean B 又依赖 Bean A。Spring 通过提前暴露未完全初始化的 Bean 来解决这个问题。 以下是 Spring 解决…...

Linux 目录操作详解

Linux目录操作详解 1. 获取当前工作目录1.1 getcwd()1.2 get_current_dir_name() 2. 切换工作目录2.1 chdir() 3. 创建和删除目录3.1 mkdir()3.2 rmdir() 4. 获取目录中的文件列表4.1 opendir() 打开目录4.2 readdir() 读取目录内容4.3 closedir() 关闭目录 5. dirent 结构体6.…...

Elasticsearch的经典面试题及详细解答

以下是一些Elasticsearch的经典面试题及详细解答: 一、基础概念与原理 什么是Elasticsearch? 回答: Elasticsearch是一个基于Lucene的分布式搜索引擎,提供了RESTful API,支持多租户能力。它能够快速、近实时地存储、搜…...

Linux-arm(1)ATF启动流程

Linux-arm(1)ATF启动流量 Author:Once Day Date:2025年1月22日 漫漫长路有人对你微笑过嘛… 全系列文章可查看专栏: Linux实践记录_Once_day的博客-CSDN博客 参考文档: ARM Trusted Firmware分析——启动、PSCI、OP-TEE接口 Arnold Lu 博…...

C#编程:List.ForEach与foreach循环的深度对比

在C#中&#xff0c;List<T>.ForEach 方法和传统的 foreach 循环都用于遍历列表中的元素并对每个元素执行操作&#xff0c;但它们之间有一些关键的区别。 List<T>.ForEach 方法 方法签名&#xff1a;public void ForEach(Action<T> action)类型&#xff1a;…...

C语言文件操作:标准库与系统调用实践

目录 1、C语言标准库文件操作 1.1.题目要求&#xff1a; 1.2.函数讲解&#xff1a; fopen 函数原型 参数 常用的打开模式 返回值 fwrite函数 函数原型 参数 返回值 注意事项 fseek函数 函数原型 参数 返回值 fread函数 函数原型 参数 返回值 fclose 函数…...

代码随想录 栈与队列 test 7

347. 前 K 个高频元素 - 力扣&#xff08;LeetCode&#xff09; 首先想到哈希&#xff0c;用key来存元素&#xff0c;value来存出现次数&#xff0c;最后进行排序&#xff0c;时间复杂度约为o(nlogn)。由于只需求前k个&#xff0c;因此可以进行优化&#xff0c;利用堆来维护这…...

C语言练习(21)

有一行电文&#xff0c;已按下面规律译成密码&#xff1a; A→Za→Z B→Yb→y C→Xc→X 即第1个字母变成第26个字母&#xff0c;第2个字母变成第25个字母&#xff0c;第i个字母变成第&#xff08;26-i十1&#xff09;个字母。非字母字符不变。假如已知道密码是Umtorhs&…...

智能手机“混战”2025:谁将倒下而谁又将突围?

【潮汐商业评论原创】 “去年做手机比较艰难&#xff0c;几乎每个品牌都在调价、压货&#xff0c;像华为这种以前都不给我们分货的厂商&#xff0c;也开始成为我的主要库存。不过今年开头比较好&#xff0c;20号国补一开始&#xff0c;店里的人流和手机销量就明显涨了不少&…...

计算机图形学:实验一 OpenGL基本绘制

1.OpenGL的环境配置&#xff1a; 集成开发环境Visual Studio Community 2019的安装&#xff1a; 在Windows一栏选择使用C的桌面开发&#xff1b;再转到“单个组件”界面&#xff0c;在“编译器、生成工具和运行时”一栏选择用于“Windows的C CMake工具”&#xff1b;然后转到…...

二分查找题目:快照数组

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;快照数组 出处&#xff1a;1146. 快照数组 难度 7 级 题目描述 要求 实现支持下列接口的快照数组&#xff1a; SnapshotArray(int length) \textt…...

深度学习|表示学习|卷积神经网络|参数共享是什么?|07

如是我闻&#xff1a; Parameter Sharing&#xff08;参数共享&#xff09;是卷积神经网络&#xff08;CNN&#xff09;的一个重要特性&#xff0c;帮助它高效地处理数据。参数共享的本质就是参数“本来也没有变过”。换句话说&#xff0c;在卷积层中&#xff0c;卷积核的参数&…...

基于相机内参推导的透视投影矩阵

基于相机内参推导透视投影矩阵&#xff08;splatam&#xff09;&#xff1a; M c a m [ 2 ⋅ f x w 0.0 ( w − 2 ⋅ c x ) w 0.0 0.0 2 ⋅ f y h ( h − 2 ⋅ c y ) h 0.0 0 0 f a r n e a r n e a r − f a r 2 f a r ⋅ n e a r n e a r − f a r 0.0 0.0 − 1.0 0.0 ] M_…...

浅析Dubbo 原理:架构、通信与调用流程

一、Dubbo 简介 Dubbo 是阿里巴巴开源的高性能、轻量级的 Java RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;框架&#xff0c;旨在实现不同服务之间的远程通信和调用。在分布式系统中&#xff0c;不同服务可能部署在不同的服务器上&#xff0c;D…...

03垃圾回收篇(D3_垃圾收集器的选择及相关参数)

目录 学习前言 一、收集器的选择 二、GC日志参数 三、垃圾收集相关的常用参数 四、内存分配与回收策略 1. 对象优先在Eden分配 2. 大对象直接进入老年代 3. 长期存活的对象将进入老年代 4. 动态对象年龄判定 5. 空间分配担保 学习前言 本章主要学习垃圾收集器的选择及…...

一、引论,《组合数学(第4版)》卢开澄 卢华明

零、前言 发现自己数数题做的很烂&#xff0c;重新学一遍组合数学吧。 参考卢开澄 卢华明 编著的《组合数学(第4版)》&#xff0c;只打算学前四章。 通过几个经典问题来了解组合数学所研究的内容。 一、幻方问题 据说大禹治水之前&#xff0c;河里冒出来一只乌龟&#xff0c…...

Vue3+TS 实现批量拖拽文件夹上传图片组件封装

1、html 代码&#xff1a; 代码中的表格引入了 vxe-table 插件 <Tag /> 是自己封装的说明组件 表格列表这块我使用了插槽来增加扩展性&#xff0c;可根据自己需求&#xff0c;在组件外部做调整 <template><div class"dragUpload"><el-dialo…...

二叉树的所有路径(力扣257)

因为题目要求路径是从上到下的&#xff0c;所以最好采用前序遍历。这样可以保证按从上到下的顺序将节点的值存入一个路径数组中。另外&#xff0c;此题还有一个难点就是如何求得所有路径。为了解决这个问题&#xff0c;我们需要用到回溯。回溯和递归不分家&#xff0c;每递归一…...

Python OrderedDict 实现 Least Recently used(LRU)缓存

OrderedDict 实现 Least Recently used&#xff08;LRU&#xff09;缓存 引言正文 引言 LRU 缓存是一种缓存替换策略&#xff0c;当缓存空间不足时&#xff0c;会移除最久未使用的数据以腾出空间存放新的数据。LRU 缓存的特点&#xff1a; 有限容量&#xff1a;缓存拥有固定的…...

LabVIEW项目中的工控机与普通电脑选择

工控机&#xff08;Industrial PC&#xff09;与普通电脑在硬件设计、性能要求、稳定性、环境适应性等方面存在显著差异。了解这些区别对于在LabVIEW项目中选择合适的硬件至关重要。下面将详细分析这两种设备的主要差异&#xff0c;并为LabVIEW项目中的选择提供指导。 ​ 硬件设…...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

Modbus RTU与Modbus TCP详解指南

目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...

Axure 下拉框联动

实现选省、选完省之后选对应省份下的市区...