spring 循环依赖

server/2024/12/3 6:50:27/

单例bean循环依赖:

先创建ABean

1.0. 记录正在创建ABean singletonCurrentlyInCreation<ABean>
1.1.实例化ABean–>得到一个对象–>存入singletonFactories
1.2. 填充BBean属性–>去单例池找BBean–>没有就创建
1.3. 进行其他依赖注入
1.4. 初始化前,初始化
1.5. 初始化后
1.6. 放入单例池

创建BBean
2.1. 实例化BBean–>得到一个对象
2.2. 填充ABean属性–>去单例池找ABean–>没有就判断creatingSet–>出现循环依赖–>earlySingletonObjects–>singletonFactories–>Lambda表达式–>执行–> 执行结果创建ABean代理对象或者ABean原始对象,存入earlySingletonObject
2.3. 进行其他依赖注入
2.4. 初始化前,初始化
2.5. 初始化后
2.6. 放入单例池

创建CBean
3.1. 实例化BBean–>得到一个对象
3.2. 填充ABean属性–>去单例池找ABean–>没有就判断creatingSet–>出现循环依赖–>从earlySingletonObjec获取ABean
3.3. 进行其他依赖注入
3.4. 初始化前,初始化
3.5. 初始化后
3.6. 放入单例池

在ABean执行到1.2时会去创建BBean,进入到BBean的生命周期,造成循环依赖

spring为了解决循环依赖进行了三级缓存

1.singletonObjects 单例池
2.earlySingletonObjects 没有经过完整生命周期的单例对象,主要作用时保存单例bean对象,而跳出循环依赖
3.singletonFactories 实例化后没有经过依赖注入的对象,主要作用时生成单例bena对象,并将其存入earlySingletonObjects

//this.getEarlyBeanReference(beanName, mbd, bean)判断是否需要AOP,需要返回AOP代理对象,否则返回原始对象
//该逻辑在bean对象实例化后,依赖注入前
this.addSingletonFactory(beanName, () -> {return this.getEarlyBeanReference(beanName, mbd, bean);});

辅助

  1. singletonCurrentlyInCreation
  2. earlyProxyReferences
@Nullable
//从单例池获取bean
protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);//判断bean是否在创建中if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {//从二级缓存earlySingletonObjects中获取bean对象singletonObject = this.earlySingletonObjects.get(beanName);//没有获取到bean对象并且允许循环依赖if (singletonObject == null && allowEarlyReference) {synchronized(this.singletonObjects) {singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {//从二级缓存earlySingletonObjects中获取bean对象singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {//从三级缓存singletonFactories中获取singletonFactory对象ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);if (singletonFactory != null) {//执行获取bean对象的逻辑singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;
}

初始化后生成的代理对象和循环依赖生成的AOP代理对象不同导致的问题:

//以下逻辑在bean实例化之后
boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);if (earlySingletonExposure) {if (this.logger.isTraceEnabled()) {this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");}//进行三级缓存this.addSingletonFactory(beanName, () -> {return this.getEarlyBeanReference(beanName, mbd, bean);});}Object exposedObject = bean;try {//依赖注入this.populateBean(beanName, mbd, instanceWrapper);//bean初始化,返回bean对象,如果bean因为AOP以外的代理对象生成,且返回的是该对象exposedObject = this.initializeBean(beanName, exposedObject, mbd);} catch (Throwable var18) {if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {throw (BeanCreationException)var18;}throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);}if (earlySingletonExposure) {//从单例池拿bean对象,如果因为循环依赖则会从二级缓存中拿到AOP生成的代理对象Object earlySingletonReference = this.getSingleton(beanName, false);if (earlySingletonReference != null) {//如果是循环依赖生成的earlySingletonReference,判断exposedObject是否非AOP代理对象if (exposedObject == bean) {exposedObject = earlySingletonReference;} else if (!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) {String[] dependentBeans = this.getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet(dependentBeans.length);String[] var12 = dependentBeans;int var13 = dependentBeans.length;for(int var14 = 0; var14 < var13; ++var14) {String dependentBean = var12[var14];if (!this.removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}//如果是不同的代理对象,则提示ERROR(避免最后生成的对象不是AOP代理对象,但是循环依赖的BBean依赖注入的是ABean因为AOP生成的代理对象,可以通过在ABean的BBean属性上添加@Laze,则ABean中添加@Async(某些PostProcessor会新生成代理对象,@Transactional不是实现PostProcessor生成代理对象,则不会出现问题)的方法也不会抛出ERROR)if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");}}}}

@Lazy解决循环依赖

@Lazy注解会在bean进行依赖注入(以上步骤的第二步)时生成一个代理对象,不会影响bean之后的流程,则不会出现循环依赖。

原型bean循环依赖

原型bean无法解决循环依赖问题。

构造器的循环依赖

无法实例化对应bean则直接提示ERROR,可在构造器加@Lazy解决。


http://www.ppmy.cn/server/41758.html

相关文章

Java毕业设计 基于SpringBoot vue新能源充电系统

Java毕业设计 基于SpringBoot vue新能源充电系统 SpringBoot 新能源充电系统 功能介绍 首页 图片轮播 充电桩 充电桩类型 充电桩详情 充电桩预约 新能源公告 公告详情 登录注册 个人中心 余额充值 修改密码 充电桩报修 充电桩预约订单 客服 后台管理 登录 个人中心 修改密码…

单片机的讲解

由于我是一个初中生没有太多时间写文章所以抱歉啊各位csdn用户 编写单片机代码教程需要考虑到读者的基础水平和学习目标。以下是一个简单的单片机&#xff08;比如基于AVR系列的Arduino&#xff09;代码教程的大纲&#xff1a; ### 第1节&#xff1a;入门 - **介绍单片机编程…

【概况】——物联网

物联网产品架构&#xff1f; 对于物联网&#xff0c;一般可分为四层&#xff0c;感知层、网络层、平台层、应用层。 1&#xff09;感知层 通过传感技术&#xff0c;采集物理世界的数据。 包含RFID&#xff08;射频识别技术&#xff09;&#xff0c;如高速公路的我们车上的E…

数学建模(科普)

数学建模&#xff0c;就是根据实际问题来建立数学模型&#xff0c;对数学模型来进行求解&#xff0c;然后根据结果去解决实际问题。 当需要从定量的角度分析和研究一个实际问题时&#xff0c;人们就要在深入调查研究、了解对象信息、作出简化假设、分析内在规律等工作的基础上…

机器学习—决策树

信息熵 小白的机器学习学习笔记 2024/5/14 15:06 文章目录 信息熵条件熵 决策树决策树量化纯度决策树生成算法ID3C4.5CART 分类示例步骤代码结果数据预览 回归示例代码结果数据预览 比特化 等概率时花费的时间比较多 随机变量不是等概率出现的时候&#xff0c;bit更小&#x…

精通Linux中的编辑器(非常详细!!!)

今天我们来说一下编辑器…… Linux中的编辑器 vi&#xff1a;是一个文本编辑器&#xff0c;用于撰写文档&#xff0c;或者开发程序。 vim&#xff1a;是vi的增强版功能一致&#xff0c;可视化效果更好一些。去鼠标化编辑更加方便可定制化 注意&#xff1a;vim编辑器是一个模式…

Kafka基础架构详解

Kafka基础架构 Kafka概述 1. Producer&#xff08;生产者&#xff09;&#xff1a; 生产者是向 Kafka broker 发送消息的客户端。它负责将消息发布到指定的主题&#xff08;Topic&#xff09;&#xff0c;并可以选择将消息发送到特定的分区&#xff08;Partition&#xff09…

phpmyadmin配置文件权限错误

错误信息 配置文件权限错误,不应任何用户都能修改! 解决办法 找到phpmyadmin所在目录 给phpmyadmin目录授权755 chmod -R 755 phpmyadmin验证服务是否可以正常访问