Spring IoC循环依赖问题

news/2025/2/16 7:01:56/

什么是循环依赖

循环依赖其实就是循环引⽤,也就是两个或者两个以上的 Bean 互相持有对⽅,最终形成闭环。⽐如A
依赖于B,B依赖于C,C⼜依赖于A。
在这里插入图片描述
注意,这⾥不是函数的循环调⽤,是对象的相互依赖关系。循环调⽤其实就是⼀个死循环,除⾮有终结
条件。
Spring中循环依赖场景有:

  • 构造器的循环依赖(构造器注⼊)
  • Field 属性的循环依赖(set注⼊)

其中,构造器的循环依赖问题⽆法解决,只能拋出 BeanCurrentlyInCreationException 异常,在解决
属性循环依赖时,spring采⽤的是提前暴露对象的⽅法

循环依赖处理机制

单例 bean 构造器参数循环依赖(⽆法解决)

prototype 原型 bean循环依赖(⽆法解决)

对于原型bean的初始化过程中不论是通过构造器参数循环依赖还是通过setXxx⽅法产⽣循环依
赖,Spring都 会直接报错处理。
AbstractBeanFactory.doGetBean()⽅法:

if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName); 
protected boolean isPrototypeCurrentlyInCreation(String beanName) { 
Object curVal = this.prototypesCurrentlyInCreation.get(); 
return (curVal != null &&(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) 
curVal).contains(beanName))));
}

在获取bean之前如果这个原型bean正在被创建则直接抛出异常。原型bean在创建之前会进⾏标记
这个beanName正在被创建,等创建结束之后会删除标记

try {
//创建原型bean之前添加标记 
beforePrototypeCreation(beanName); 
//创建原型bean
prototypeInstance = createBean(beanName, mbd, args); 
}
finally {
//创建原型bean之后删除标记 
afterPrototypeCreation(beanName);
}

总结:Spring 不⽀持原型 bean 的循环依赖。

单例bean通过setXxx或者@Autowired进⾏循环依赖

Spring 的循环依赖的理论依据基于 Java 的引⽤传递,当获得对象的引⽤时,对象的属性是可以延后设置的,但是构造器必须是在获取引⽤之前Spring通过setXxx或者@Autowired⽅法解决循环依赖其实是通过提前暴露⼀个ObjectFactory对象来完成的,简单来说ClassA在调⽤构造器完成对象初始化之后,在调⽤ClassA的setClassB⽅法之前就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring容器中

循环依赖源码分析

在这里插入图片描述
在这里插入图片描述

(1)A实例化过程之后立马放入三级缓存(提前暴露自己)

refresh -> finishBeanFactoryInitialization-> getBean -> doGetBean -> createBean -> doCreateBean中

在这里插入图片描述

  • 判断A存在循环依赖,将A对象放入到三级缓存singletonFactories
	// 解决循环依赖的关键步骤,判断是否存在循环依赖:单例 && 允许循环依赖 && 当前bean正在创建中boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));// 如果需要提前暴露单例Bean,则将该Bean放入三级缓存中if (earlySingletonExposure) {if (logger.isDebugEnabled()) {logger.debug("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}// 将刚创建的bean放入三级缓存中singleFactories(key是beanName,value是FactoryBean)//如果支持则暴露一个工厂addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}
	protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {/*** 如果单例池当中不存在才会add* 因为这里主要为了循环依赖服务的代码* 如果bean存在单例池的话其实已经是一个完整的bean了* 一个完整的bean自然也是已经完成属性注入,循环依赖已经依赖上了* 所以如果这个对象已经是一个完整的bean,就不需要关心,不需要进入 if* 如果一级缓存中没有该实例,则放入三级缓存中*/if (!this.singletonObjects.containsKey(beanName)) {// 放入三级缓存this.singletonFactories.put(beanName, singletonFactory);/*** 从三级缓存中remove掉当前的bean* 为什么要remove?抛开细节,这三个map当中其实其实存的都是一个对象* spring的做法是三个不能同时存在*/// 从二级缓存中移除this.earlySingletonObjects.remove(beanName);//将beanName注册到registeredSingletons缓存(已经注册的单例集合)this.registeredSingletons.add(beanName);}}}
  • A对象中需要进行属性注入,发现依赖了B对象

在这里插入图片描述
在这里插入图片描述

(2)B对象实例化过程和A实例化过程一样,将自己放入到三级缓存中之后下一步进行属性注入发现依赖于A,那么就去三级缓存使用尚未成型的Bean A,并将A升级放到二级缓存中。

  • B对象实例化放入三级缓存中

在这里插入图片描述

  • B对象中需要进行属性注入,发现依赖了A对象

在这里插入图片描述
在这里插入图片描述

(3) 再次加工Bean A的过程以及放入二级缓存示意图

在这里插入图片描述

(4)B对象创建完成之后放入一级缓存中

在这里插入图片描述
(5)A对象从一级缓存中获取到B对象,注入到A对象中
在这里插入图片描述


http://www.ppmy.cn/news/35835.html

相关文章

webRTC直播为什么比RTMP快?

Nginx-RTMP存在的问题 首先是延迟较高&#xff0c;大约是1.7秒&#xff0c;在扭转摄像头后观察画面&#xff0c;有非常明显的滞后感。 当页面处于后台时&#xff0c;浏览器分配的解码算力有所下降&#xff0c;延迟会有累加情况。 相比于刚才17:09时的1.7秒延迟&#xff0c;此…

运动蓝牙耳机哪个品牌好?运动蓝牙耳机推荐

国内运动耳机品牌的兴起对外国品牌带了不小的冲击。在国内运动消费市场快速增长的今天&#xff0c;运动耳机市场的竞争也是相当的激烈。谁能吃下这块大蛋糕&#xff0c;当然当然是要用实力说话了。今天小编就来盘点一下市场上的明星产品&#xff0c;是骡子是马拉出来溜溜。 1、…

EPPlus电子表格的.NET库Crack

EPPlus电子表格的.NET库Crack 改进了数据验证中的性能和对跨工作表引用的支持。 EPPlus是一个用于管理Office Open XML电子表格的.NET库。该库的设计考虑到了开发人员&#xff0c;使任何了解Microsoft Excel或任何其他电子表格库的开发人员都可以轻松地使用API。EPPlus为Micros…

DRF 使用djangorestframework-jwt 报错

报错信息 ImportError: Could not import rest_framework_jwt.authentication.JSONWebTokenAuthentication for API setting DEFAULT_AUTHENTICATION_CLASSES. ImportError: cannot import name smart_text from django.utils.encoding原因 JSON Web Token不再维护&#xff0c…

【Spring Cloud Alibaba】9.分布式配置管理(Nacos Config)

文章目录简介分布式配置中心Nacos ConfigNacos设置微服务设置pom修改添加配置文件测试项目修改Nacos配置添加测试接口启动项目配置动态更新测试简介 接下来对分布式项目实现统一配置管理&#xff0c;本操作先要完成之前的步骤&#xff0c;详情请参照【Spring Cloud Alibaba】S…

(Java) 马虎的算式

题目描述&#xff1a; 小明是个急性子&#xff0c;上小学的时候经常把老师写在黑板上的题目抄错了。 有一次&#xff0c;老师出的题目是&#xff1a;36 x 495 ? 他却给抄成了&#xff1a;396 x 45 ? 但结果却很戏剧性&#xff0c;他的答案竟然是对的&#xff01;&#xff…

【无标题】ES3J是什么二极管?参数特性怎么看?

二极管可以分为很多种类&#xff0c;有整流二极管、肖特基二极管、&#xff08;超&#xff09;快恢复二极管、开关二极管、TVS二极管、ESD静电保护二极管、发光二极管等等。那么&#xff0c;不同类型的二极管&#xff0c;它们之间有什么区别呢&#xff1f;接下来&#xff0c;东…

NVT | NT96660 NVTIPC库应用说明

NVT | NT96660 NVTIPC库应用说明 时间:2023-03-28 文章目录 NVT | `NT96660` NVTIPC库应用说明1.介绍2.特色3.消息队列框架4.使用4-1.IPC消息队列操作4-2.IPC信号操作4-3.IPC简单流程4-4.IPC简单代码eCos例程uITRON历程4-5.`IPC`系统命令4-5-1.开启一个`Linux`进程在`uITRON`…