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

Spring解决循环依赖

Spring框架为了解决循环依赖问题,设计了一套三级缓存机制:

  • 一级缓存singletonObjects:这个是最常规的缓存,用于存放完成初始化好的bean,如果某个bean已经在这个缓存了直接返回。
  • 二级缓存earlySigletonObjects:这个用于存放早期暴露出来的bean,就是那些创建出来还没有初始化好的bean,这样做的目的就是为了bean创建过程中能提前暴露出来,方便解决循环依赖的问题。
  • 三级缓存 singletonFactories:这个缓存存放的是bean的工厂对象,这个工厂对象负责bean的实例,当一个bean创建时,它的工厂对象会被放入缓存中。

Spring解决循环依赖

三级缓存

  • singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用

  • earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖

  • singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖

整体流程
  1. 首先A完成初始化第一步先将自己提前曝光出来(通过ObjectFactory将自己提前曝光出来),在初始化的时候,发现自己需要依赖B,就开始尝试get(B),这时候发现B还没有创建;

  2. B走创建流程,在B初始化的时候依赖C,C还没有创建出来;

  3. C开始初始化,在C初始化的时候发现自己依赖A,于是尝试get(A),这时候由于A已经添加到缓存中了(一般都是添加到三级缓存中singletonFactory),通过ObjectFactory提前曝光,所以可以通过ObjectFactory#getObject()获取到A对象。C拿着A对象后顺利初始化,然后自己添加到一级缓存中;

  4. 回到B,B也拿到C对象,完成初始化,A可以顺利拿到B,这里整个链路已经完成初始化过程了。

关键字:三级缓存,提前曝光

再来一个例子

假设现在有两个bean,一个A,一个B;

Spring容器开始创建A对象,会先去一级缓存中查看是否有BeanA的实例,如果没有就会创建一个A的实例,并将其工厂对象放入三级缓存中,然后BeanA 的创建因为需要注入B而被挂起,Spring开始创建BeanB对象。

BeanB同样回去一级缓存中查找是否存在B实例,由于还没有创建,Spring会将bean B的半成品放入二级缓存,继续创建买这个B需要依赖A,由于A的工厂对象已经放在三级缓存中了,spring可以直接获取三级对象中的beanA的工厂对象,通过它来创建beanA的实例。

这样,即使两个 beans 相互依赖,Spring 也能够通过三级缓存机制成功地创建它们,解决了循环依赖的问题。

仅有二级缓存无法解决涉及AOP代理的循环依赖问题。

为什么二级缓存不可以

二级缓存earlySingletonObjects用于存储半成品的Bean实例,即那些已经被实例化但尚未完成初始化(如属性填充和方法调用)的Bean。这个缓存允许Spring在Bean的创建过程中就能提前暴露出来,以便于解决循环依赖的问题。然而,如果只有二级缓存,当涉及到AOP代理时,问题就来了。

AOP代理的生成是在Bean的初始化阶段完成的,这意味着在Bean的所有属性都被设置之后。如果一个Bean需要被代理,那么在代理之前,Spring会尽量从缓存中获取到原始的Bean实例,以避免在代理过程中出现循环引用的问题。但是,如果只有二级缓存,那么在Bean初始化之前,我们无法从缓存中获取到代理对象,因为二级缓存中存储的是尚未初始化的Bean实例,而不是代理对象。

例子

假设有两个Bean,A和B,它们相互依赖,并且A需要被AOP代理。在Spring的创建过程中,首先会创建A的实例并将其放入二级缓存中。然后,当尝试创建B并注入A时,会发现A还没有完成初始化,因此无法生成A的代理对象。这样就会导致循环依赖的问题无法被解决。

而三级缓存中的singletonFactories存储的是Bean的工厂对象,可以在Bean初始化之前就生成代理对象,并将其放入一级缓存singletonObjects中。这样,即使Bean之间存在循环依赖,Spring也能够通过三级缓存机制成功地创建它们,解决了循环依赖的问题。

总的来说,三级缓存机制是Spring为了在保持设计原则的同时,解决循环依赖和AOP代理的问题而设计的。二级缓存虽然可以解决部分循环依赖的问题,但在面对AOP代理时就显得力不从心了。因此,Spring需要三级缓存来确保在复杂情况下依然能够正常工作。

相关文章:

  • Ansys Mechanical|组装 External Mechanical Model
  • c++中 unordered_map 与 unordered_set 用法指南
  • 深入分析 Android BroadcastReceiver (一)
  • pqgrid的使用
  • 如何快速找到 RCE
  • 情感读本期刊万方收录综合期刊投稿
  • flinksql 回撤流中主键发生变更的影响(group by中的值发生改变)
  • Go-知识并发控制Context
  • NextJs 渲染篇 - 什么是CSR、SSR、SSG、ISR 和服务端/客户端组件
  • 设计模式(四)原型模式
  • 线性代数|机器学习-P2 A的列向量空间
  • 如何解决Mac系统创建/home目录提示Read-Only filesystem(补充)?
  • Flutter 中的 SliverMainAxisGroup 小部件:全面指南
  • Flutter 中的 SliverOpacity 小部件:全面指南
  • MMPose-RTMO推理详解及部署实现(上)
  • -------------------- 第二讲-------- 第一节------在此给出链表的基本操作
  • 2017年终总结、随想
  • ES6之路之模块详解
  • flutter的key在widget list的作用以及必要性
  • HTTP传输编码增加了传输量,只为解决这一个问题 | 实用 HTTP
  • macOS 中 shell 创建文件夹及文件并 VS Code 打开
  • rabbitmq延迟消息示例
  • 阿里云购买磁盘后挂载
  • 从@property说起(二)当我们写下@property (nonatomic, weak) id obj时,我们究竟写了什么...
  • 后端_MYSQL
  • 如何选择开源的机器学习框架?
  • 思否第一天
  • 算法-图和图算法
  • 在weex里面使用chart图表
  • 3月27日云栖精选夜读 | 从 “城市大脑”实践,瞭望未来城市源起 ...
  • ​水经微图Web1.5.0版即将上线
  • ​总结MySQL 的一些知识点:MySQL 选择数据库​
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • #设计模式#4.6 Flyweight(享元) 对象结构型模式
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • $.ajax()
  • (33)STM32——485实验笔记
  • (C)一些题4
  • (void) (_x == _y)的作用
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (二十六)Java 数据结构
  • (附源码)spring boot公选课在线选课系统 毕业设计 142011
  • (转)MVC3 类型“System.Web.Mvc.ModelClientValidationRule”同时存在
  • .net core Redis 使用有序集合实现延迟队列
  • .NET Core WebAPI中使用swagger版本控制,添加注释
  • .net 按比例显示图片的缩略图
  • .net 获取某一天 在当月是 第几周 函数
  • .NET(C#、VB)APP开发——Smobiler平台控件介绍:Bluetooth组件
  • .NET分布式缓存Memcached从入门到实战
  • @Async注解的坑,小心
  • [ 网络基础篇 ] MAP 迈普交换机常用命令详解
  • []FET-430SIM508 研究日志 11.3.31
  • [Android]通过PhoneLookup读取所有电话号码
  • [Angular 基础] - 自定义指令,深入学习 directive
  • [ArcPy百科]第三节: Geometry信息中的空间参考解析