Spring—循环依赖与三级缓存

news2025/4/26 0:48:43

Spring中存在三级缓存:

  • 第一层缓存(singletonObjects):单例对象缓存池,已经实例化并且属性赋值,这里的对象是成熟对象
  • 第二层缓存(earlySingletonObjects):单例对象缓存池,已经实例化但尚未set属性赋值(Bean的生命周期第二步),这里的对象是半成品对象
  • 第三层缓存(singletonFactories): 单例工厂的缓存,里面放的是工厂对象(实际上就是一串lambda),每个工厂可以创建对应的Bean

只有单例才会三级缓存这套东西,多例的话每次getBean都直接重新实例化一个!

Spring生成Bean的流程

假使 A 和 B 发生了循环依赖:

  1. 先往createingSet(一个Set集合,记录正在创建的Bean,用于后续判断是否出现循环依赖)放入A
  2. 一级缓存中没找到A,通过无参构造实例化A,将A有关的一串lambda表达式放入三级缓存(此时A的实例化和属性注入就已经分离开了),set属性赋值注入B
  3. 通过createingSet发现没有B,知道此时还没出现循环依赖,那就去一级缓存找B,找不到,往createingSet放入B,通过无参构造实例化B,将B有关的一串lambda表达式放入三级缓存,进行set属性注入A
  4. 通过createingSet发现A,就知道发生了循环依赖!那就去二级缓存(不是一级缓存了,因为发生了循环依赖,逻辑需要有变化)找,没找到,去三级缓存找,找到有关Alambda表达式,执行lambda:
    • 如果A需要被AOP,则此时提前对其AOP(正常Bean的生命周期下应该是在初始化后AOP),将AOP后的代理对象放入二级缓存,并从三级缓存中删除(这样能保证AOP后的代理对象是单例的,不会被多次生成,因为lambda相当于一次性的消耗品了,同时也会上锁保证线程安全),并将代理对象放入earlyProxyReferences(用于存储有哪些对象提前AOP了,后续有用)
    • 如果A不需要被AOP,则生成A的普通对象并放入二级缓存,并从三级缓存中删除
  1. 此时B拿到了对象A(可能是代理的,也可能是普通的)进行set注入,进行B的其它Bean生命周期,当Bean成熟之后放入一级缓存移除二、三级缓存(实际上此时二级缓存中没有B,三级缓存中有B
  2. 此时返回到第2步,A就拿到了B的成熟对象,set注入到A中,然后进行A的其它Bean生命周期
  3. A的其它生命周期包括AOP(Spring中AOP是在Bean后置处理器的after方法中),但这时A可能已经在lambda被提前AOP了,这就需要通过之前的earlyProxyReferences判断A有没有提前AOP,防止重复AOP
  4. 将成熟的A放入一级缓存移除二、三级缓存(实际上此时三级缓存中没有A,二级缓存中有A

如果 A 和 B 发生循环依赖的同时,A 又和 C 发生了循环依赖:

在A中注入B之后,要注入C,C从createingSet中发现有A,就知道发生了循环依赖,那就去二级缓存找,这时候能找得到,因为已经在B进行初始化的时候从三级缓存中生成了一个A并放入到了二级缓存,那么就直接拿这个二级缓存中的A注入,然后走C的其它Bean生命周期,将C放入一级缓存

各级缓存的作用:

  • 一级缓存:总得有个单例池用来保存你所有成熟的Bean吧?不然你BeanFactory从哪获取Bean?这个单例池就是一级缓存
  • 二级缓存:用来保证Bean是单例的。因为你三级缓存中的lambda应该是一次性的消耗品,以此保证只会生成一个Bean,那么这个生成的半成品Bean就先放入二级缓存中缓存起来。用于存放提前曝光的不成熟的Bean
  • 三级缓存:用来打破循环并解决出现AOP的情况下的循环依赖。当发生了循环依赖,我们可以保证你能从三级缓存中得到Bean(因为单例Bean不管怎样都会先往三级缓存中存个lambda),通过三级缓存中的lambda,我们可以生成半成品普通Bean或者代理Bean,再放入二级缓存,并从三级缓存中删除,这就意味着三级缓存中的lambda是一次性的消耗品,这样就可以保证Bean的单例

一级缓存能解决循环依赖吗?

将半成品和成品都放入一级缓存,这样也能解决普通的循环依赖,但可能造成一些空指针异常,毕竟里面有半成品;而且混杂成品和半成品在一起,不太优雅;对于出现AOP的循环依赖无法解决

可不可以使用一、二级缓存解决循环依赖?

可以,但这样如果出现AOP的话,在对象实例化时就要完成AOP代理,生成代理对象,再进行属性注入,即先创建代理对象,再对代理对象进行属性注入,这与Spring设计理念中“Bean的初始化和代理解耦”是不符的

那么就可以通过三级缓存中的lambda判断,只有当出现AOP + 循环依赖的时候,才提前创建代理对象,然后进行依赖注入初始化,否则创建普通对象,走正常的Bean生命周期

可不可以使用一、三级缓存解决循环依赖?

不可以,因为使用到三级缓存这套东西就说明Bean是单例的,如果用三级缓存,那么每次使用三级缓存的lambda生成的半成品Bean都不是同一个,当出现AB发生循环依赖AC也发生循环依赖的这种情况,B中的AC中的A不是同一个了!必须要让三级缓存中的lambda变为一次性的,并且有个地方存储这个一次性生成出来的半成品,这个地方就是二级缓存

Spring如何解决循环依赖?

createingSet(判断是否出现循环依赖) + 三级缓存(解决循环依赖) + earlyProxyReferences(防止解决循环依赖的过程中重复AOP

哪些循环依赖默认情况下无法解决?

  • 构造注入下的循环依赖是无法解决的,因为在实例化的时候就需要强制注入

  • 两个都是多例对象。因为多例不会放入缓存,多实例Bean是每次调用一次getBean都会执行一次构造方法并且给属性赋值,根本没有三级缓存,因此不能解决循环依赖。

  • 一个单例,一个多例。如果先实例化多例,解决不了。如果先实例化单例,可以解决

其它循环依赖如何解决:

@Lazy注解:

@Lazy可以出现在类上、方法上、构造器上、方法参数上、成员变量上。当在方法上时,通常与@Bean一起使用

@Lazy注解可以解决构造器注入情况下的循环依赖。当A的构造方法上出现@Lazy,要注入B时,Spring会生成一个B的代理对象,这个代理对象和B没什么两样,继承于B,同时聚合了一个B对象(原始B,这时A就可以直接实例化,然后注入到原始的B中

这时,A.getB不等于原始B,因为A中的B是代理B

但是A.getB.getA却是最开始的A,代理B中的getA方法其实是调用了代理B中聚合的原始B中的getA方法,同样,代理B的其它方法也是调用了原始B的其它方法

所以当A要用B中的方法时,虽然不是直接调用原始B的方法,但其实通过代理B还是间接调用到了原始B中的方法,当代理B要调用A中的方法时,会先通过聚合的原始B获取到A,然后调用A中的方法。

综上,多出一个代理B其实没有任何影响,但是这种方式可以直接让A实例化,直接就避免出现了循环依赖

同时,被@Lazy标注的Bean是懒加载的,正常情况下Bean在Spring容器启动后就会全部创建,但被@Lazy标注的Bean只有在被使用到时才会创建

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2342817.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux网络】构建UDP服务器与字典翻译系统

📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…

【PGCCC】Postgres 故障排除:修复重复的主键行

如何从表中删除不需要的重复行。这些重复行之所以“不需要”,是因为同一个值在指定为主键的列中出现多次。自从 glibc 好心地改变了排序方式后,我们发现这个问题有所增加。当用户升级操作系统并修改底层 glibc 库时,这可能会导致无效索引。 唯…

DeepSeek+Cursor+Devbox+Sealos项目实战

黑马程序员DeepSeekCursorDevboxSealos带你零代码搞定实战项目开发部署视频教程,基于AI完成项目的设计、开发、测试、联调、部署全流程 原视频地址视频选的项目非常基础,基本就是过了个web开发流程,但我在实际跟着操作时,ai依然会…

996引擎-拓展变量:物品变量

996引擎-拓展变量:物品变量 测试代码参考资料对于Lua来说,只有能保存数据库的变量才有意义。 至于临时变量,不像TXT那么束手束脚,通常使用Lua变量就能完成。 SELECT * FROM dbo.TBL_ITEM_EX_ABIL WHERE FLD_MAKEINDEX = 28620 <

【踩坑记录】stm32 jlink程序烧录不进去

最近通过Jlink给STM32烧写程序时一直报错&#xff0c;但是换一个其他工程就可以烧录&#xff0c;对比了一下jink配置&#xff0c;发现是速率选太高了“SW Device”&#xff0c;将烧录速率调整到10MHz以下就可以了

‌RISC-V低功耗MCU动态时钟门控技术详解

我来分享一下RISC-V核低功耗MCU的动态时钟门控技术实现&#xff1a; 这款MCU通过硬件级时钟门控电路实现了模块级的功耗管理。当外设&#xff08;如UART、SPI&#xff09;处于闲置状态时&#xff0c;系统会自动切断其时钟信号&#xff0c;减少无效翻转功耗。同时支持多电压域协…

工厂模式:解耦对象创建与使用的设计模式

工厂模式&#xff1a;解耦对象创建与使用的设计模式 一、模式核心&#xff1a;封装对象创建逻辑&#xff0c;客户端无需关心具体实现 在软件开发中&#xff0c;当创建对象的逻辑复杂或频繁变化时&#xff0c;直接在客户端代码中 new 对象会导致耦合度高、难以维护。例如&…

Python爬虫学习:高校数据爬取与可视化

本项目实现了从中国教育在线&#xff08;eol.cn&#xff09;的公开 API 接口爬取高校相关数据&#xff0c;并对数据进行清洗、分析与可视化展示。主要包括以下功能&#xff1a; 爬取高校基础信息及访问量数据数据清洗与格式转换多维度数据分析与可视化&#xff0c;如高校数量分…

触觉智能RK3506核心板,工业应用之RK3506 RT-Linux实时性测试

在工业自动化、机械臂控制等高实时性场景中&#xff0c;系统响应速度与稳定性直接决定设备效能。触觉智能RK3506核心板基于瑞芯微三核Cortex-A7架构深度优化&#xff0c;搭载Linux 6.1内核并支持Linux-RT实时系统&#xff0c;提供实时性能的高性价比解决方案。 RK3506与RT-Linu…

基于SpringBoot的高校体育馆场地预约管理系统-项目分享

基于SpringBoot的高校体育馆场地预约管理系统-项目分享 项目介绍项目摘要目录总体功能图用户实体图赛事实体图项目预览用户个人中心医生信息管理用户管理场地信息管理登录 最后 项目介绍 使用者&#xff1a;管理员 开发技术&#xff1a;MySQLJavaSpringBootVue 项目摘要 随着…

华为云获取IAM用户Token的方式及适用分析

&#x1f9e0; 一、为什么要获取 IAM 用户 Token&#xff1f; 我们用一个生活中的比喻来解释&#x1f447;&#xff1a; &#x1f3e2; 比喻场景&#xff1a; 你要去一个 高级写字楼&#xff08;华为云物联网平台&#xff09; 办事&#xff08;调用接口管理设备&#xff09;&…

如何利用快照与备份快速恢复服务器的数据

在服务器上利用**快照&#xff08;Snapshot&#xff09;**和**备份&#xff08;Backup&#xff09;**快速恢复数据&#xff0c;可显著减少停机时间并确保业务连续性。以下是具体操作步骤和最佳实践&#xff1a; --- ### **1. 快照&#xff08;Snapshot&#xff09;恢复** **适…

Git 详细使用说明文档(适合小白)

Git 详细使用说明文档&#xff08;适合小白&#xff09; 1. 什么是 Git&#xff1f; Git 是一个版本控制系统&#xff0c;帮助你管理和跟踪代码的变更。无论是个人项目还是团队协作&#xff0c;Git 都能帮助你记录代码的历史版本&#xff0c;方便回溯和协作。 2. 安装 Git …

Graph Database Self-Managed Neo4j 知识图谱存储实践1:安装和基础知识学习

Neo4j 是一个原生图数据库&#xff0c;这意味着它在存储层实现了真正的图模型。它不是在其他技术之上使用“图抽象”&#xff0c;而是以您在白板上绘制想法的相同方式在Neo4j中存储数据。 自2007年以来&#xff0c;Neo4j已经发展成为一个丰富的工具、应用程序和库的生态系统。…

一天学完Servlet!!!(万字总结)

文章目录 前言Servlet打印Hello ServletServlet生命周期 HttpServletRequest对象常用api方法请求乱码问题请求转发request域对象 HttpServletResponse对象响应数据响应乱码问题请求重定向请求转发与重定向区别 Cookie对象Cookie的创建与获取Cookie设置到期时间Cookie注意点Cook…

E3650工具链生态再增强,IAR全面支持芯驰科技新一代旗舰智控MCU

近日&#xff0c;全球嵌入式软件开发解决方案领导者IAR与全场景智能车芯引领者芯驰科技正式宣布&#xff0c;IAR Embedded Workbench for Arm已全面支持芯驰E3650&#xff0c;为这一旗舰智控MCU提供开发和调试一站式服务&#xff0c;进一步丰富芯驰E3系列智控芯片工具链生态&am…

MSSQL-数据库还原报错-‘32(另一个程序正在使用此文件,进程无法访问。)‘

这里是引用 标题: Microsoft SQL Server Management Studio 还原 对于 服务器“<<服务器名称>>”失败。 (Microsoft.SqlServer.SmoExtended) 有关帮助信息&#xff0c;请单击: http://go.microsoft.com/fwlink?ProdNameMicrosoftSQLServer&ProdVer12.0.2000.8…

卷积神经网络:视觉炼金术士的数学魔法

引言&#xff1a;当数学遇见视觉炼金术 在人工智能的奇幻世界里&#xff0c;卷积神经网络&#xff08;CNN&#xff09;犹如掌握视觉奥秘的炼金术士&#xff0c;将原始像素的"铅块"淬炼成认知的"黄金"。这种融合数学严谨性与生物灵感的算法架构&#xff0c…

立马耀:通过阿里云 Serverless Spark 和 Milvus 构建高效向量检索系统,驱动个性化推荐业务

作者&#xff1a;厦门立马耀网络科技有限公司大数据开发工程师 陈宏毅 背景介绍 行业 蝉选是蝉妈妈出品的达人选品服务平台。蝉选秉持“陪伴达人赚到钱”的品牌使命&#xff0c;致力于洞悉达人变现需求和痛点&#xff0c;提供达人选高佣、稳变现、速响应的选品服务。 业务特…

专业热度低,25西电光电工程学院(考研录取情况)

1、光电工程学院各个方向 2、光电工程学院近三年复试分数线对比 学长、学姐分析 由表可看出&#xff1a; 1、光学工程25年相较于24年下降20分&#xff0c; 2、光电信息与工程&#xff08;专硕&#xff09;25年相较于24年上升15分 3、25vs24推免/统招人数对比 学长、学姐分析…