06-解决Spirng中的循环依赖问题

Bean的循环依赖问题

循环依赖: A对象中有B属性 , B对象中有A属性(丈夫类Husband中有Wife的引用, 妻子类Wife中有Husband的引用)

在这里插入图片描述

toString()方法重写时直接输出wife/husband会出现递归导致的栈内存溢出错误

  • 直接输出wife/husband会调用它们的toString()方法, 在toString()方法又会调用husband/wife的toString()方法一直循环
//丈夫类
public class Husband {private String name;private Wife wife;// 属性的setter方法// toString()方法重写时直接输出wife会出现递归导致的栈内存溢出错误,需要输出wife.getName()@Overridepublic String toString() {return "Husband{" +"name='" + name + '\'' +", wife=" + wife.getName() +'}';}
}
//妻子类
public class Wife {    private String name;private Husband husband;//属性的setter方法// toString()方法重写时不能直接输出husband,需要输出husband.getName()@Overridepublic String toString() {return "Wife{" +"name='" + name + '\'' +", husband=" + husband.getName() +'}';}
}

singleton下的set注入

Spring可以解决在singleton+setter模式下出现的循环依赖问题, 因为在这种模式下Spring对Bean的管理主要分为清晰的两个阶段

  • 实例化对象阶段:Spring容器实例化Bean的时候会先创建对象 , 不等其属性赋值就会曝光加入正在创建的bean的缓存中 , 这样其他bean就可以直接引用它
  • 对象的属性赋值阶段Bean曝光之后,再调用set方法进行属性的赋值
  • 注意: 只有在scope是singleton的情况下,Bean才会采取提前曝光的措施 , 多实例下不会曝光
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--singleton表示在整个Spring容器当中是单例的,独一无二的对象--><bean id="husbandBean" class="com.powernode.spring6.bean.Husband" scope="singleton"><property name="name" value="张三"/><property name="wife" ref="wifeBean"/></bean><bean id="wifeBean" class="com.powernode.spring6.bean.Wife" scope="singleton"><property name="name" value="小花"/><property name="husband" ref="husbandBean"/></bean>
</beans> 
public class CircularDependencyTest {@Testpublic void testSingletonAndSet(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");Husband husbandBean = applicationContext.getBean("husbandBean", Husband.class);Wife wifeBean = applicationContext.getBean("wifeBean", Wife.class);//Husband{name="张三",wife=小花}System.out.println(husbandBean);//Wife{name="小花",husband=张三}System.out.println(wifeBean);}
}

prototype下的set注入

若循环依赖的所有Bean的scope="prototype"时 , Spring无法解决它们产生的循环依赖问题,此时会出现BeanCurrentlyInCreationException异常

  • 当两个bean的scope都是prototype的时候才会出现Bean正在处于创建中异常 , 如果其中任意一个是singleton的就不会出现异常
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--由于wifeBean/husbandBean是多实例的,只有每一次执行getBean()方法的时候才会创建新的Bean,故会造成死循环--><!--ref相当于从容器中手动获取对象,由于其是多实例的,所以每次获取的都是新对象--><bean id="husbandBean" class="com.powernode.spring6.bean.Husband" scope="prototype"><property name="name" value="张三"/><property name="wife" ref="wifeBean"/></bean><bean id="wifeBean" class="com.powernode.spring6.bean.Wife" scope="prototype"><property name="name" value="小花"/><property name="husband" ref="husbandBean"/></bean><!--因为wifeBean是单实例的会提前曝光,所以husbandBean可以new新对象--><bean id="husbandBean" class="com.powernode.spring6.bean.Husband" scope="prototype"><property name="name" value="张三"/><property name="wife" ref="wifeBean"/></bean><!--当两个bean的scope都是prototype的时候才会出现异常,如果其中任意一个是singleton的就不会出现异常--><bean id="wifeBean" class="com.powernode.spring6.bean.Wife" scope="singleton"><property name="name" value="小花"/><property name="husband" ref="husbandBean"/></bean>
</beans>

singleton下的构造注入

Spring无法解决singleton下的构造注入产生的循环依赖,会出现BeanCurrentlyInCreationException异常

  • 因为构造方法注入会导致实例化对象的过程和对象属性赋值的过程没有分离开导致循环依赖, 想要实例化对象必须完成对象属性的赋值
//丈夫类
public class Husband {private String name;private Wife wife;public Husband(String name, Wife wife) {this.name = name;this.wife = wife;}// -----------------------分割线--------------------------------public String getName() {return name;}@Overridepublic String toString() {return "Husband{" +"name='" + name + '\'' +", wife=" + wife +'}';}
}
//妻子类
public class Wife {private String name;private Husband husband;public Wife(String name, Husband husband) {this.name = name;this.husband = husband;}// -------------------------分割线--------------------------------public String getName() {return name;}@Overridepublic String toString() {return "Wife{" +"name='" + name + '\'' +", husband=" + husband +'}';}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="hBean" class="com.powernode.spring6.bean2.Husband" scope="singleton"><constructor-arg name="name" value="张三"/><constructor-arg name="wife" ref="wBean"/></bean><bean id="wBean" class="com.powernode.spring6.bean2.Wife" scope="singleton"><constructor-arg name="name" value="小花"/><constructor-arg name="husband" ref="hBean"/></bean>
</beans>

Spring解决循环依赖的原理

Spring解决set + singleton模式下循环依赖的问题是将实例化Bean给Bean属性赋值这两个动作分开去完成(这两步不要求在同一个时间点上完成)

  • 先实例化单实例的Bean: 通过调用无参数构造方法时把所有的单例Bean实例化出来,放到一个Map集合(缓存) 当中曝光给外界
  • 然后给Bean属性赋值:调用setter方法来完成对象的属性赋值

Spring框架底层源码级别的缓存实现

  • 完整单例对象的缓存:key存储bean名称,value存储的单例Bean对象属性都已经赋值【一级缓存】
  • 早期单例对象的缓存:key存储bean名称,value存储早期的Bean对象属性没有赋值【二级缓存】
  • 单例工厂对象的缓存:key存储bean名称,value存储每个Bean对象对应的ObjectFactory单例工厂对象【三级缓存】

在这里插入图片描述

AbstractAutowireCapableBeanFactory类的doCreateBean()方法完成Bean的创建和属性赋值

  • creatBeanInstance只会创建Bean对象, addSingletonFactory方法中将bean对象缓存起来然后曝光, populateBean方法填充bean即给bean的属性赋值

  • DefaultSingletonBeanRegistry的addSingletonFactory()方法的作用是将创建Bean对象的ObjectFactory工厂对象提前曝光

在这里插入图片描述

spring会先从一级缓存中获取Bean,获取不到则从二级缓存中获取Bean,如果还是获取不到则从三级缓存中获取之前曝光的ObjectFactory对象,然后通过ObjectFactory对象获取Bean实例并放到二级缓存

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring的Redis客户端

如何在Spring中操作redis 在创建springboot项目的时候引入redis的依赖. 在配置文件里指定redis主机的地址和端口,此处我们配置了ssh隧道,所以连接的就是本机的8888端口. 创建一个controller类,注入操作redis的对象. 前面使用jedis,是通过jedis对象里的各种方法来操作redis的,此…

在任何机器人上实施 ROS 导航堆栈的指南

文章目录 路径规划参考 路径规划 路径规划是导航的最终目标。这允许用户向机器人给出目标姿势&#xff0c;并让它在给定的环境中自主地从当前位置导航到目标位置。这是我们迄今为止所做的一切&#xff08;地图绘制和本地化&#xff09;的汇集点。ROS 导航堆栈已经为我们完成了…

通讯协议学习之路(实践部分):SPI开发实践

通讯协议之路主要分为两部分&#xff0c;第一部分从理论上面讲解各类协议的通讯原理以及通讯格式&#xff0c;第二部分从具体运用上讲解各类通讯协议的具体应用方法。 后续文章会同时发表在个人博客(jason1016.club)、CSDN&#xff1b;视频会发布在bilibili(UID:399951374) 本文…

【PG】PostgreSQL 预写日志(WAL)、checkpoint、LSN

目录 预写式日志&#xff08;WAL&#xff09; WAL概念 WAL的作用 WAL日志存放路径 WAL日志文件数量 WAL日志文件存储形式 WAL日志文件命名 WAL内容 检查点&#xff08;checkpoint&#xff09; 1 检查点概念 2 检查点作用 触发检查点 触发检查点之后数据库操作 设置合…

四入进博会,优衣库围绕科技可持续演绎“服装进化论”

11月5日&#xff0c;第六届中国国际进口博览会在上海拉开帷幕。这些年来&#xff0c;进博巨大的平台效应&#xff0c;使其成为各个行业头部品牌的秀场&#xff0c;也持续为消费者、产业链带来惊喜。 今年&#xff0c;也是全球服装界科技知名品牌——优衣库的第四次进博之旅。从…

Python爬虫爬取家纺数据并分析

因为时间的原因&#xff0c;没法写一个详细的教程&#xff0c;但是我可以提供一个基本的框架。你需要根据实际情况进行修改和扩展。以下是使用Python的requests库和BeautifulSoup库来爬取网页内容的基本步骤&#xff1a; # 导入所需的库 import requests from bs4 import Beaut…

2023/11/13JAVA学习

字节数组增大的同时,运行速度也会加快,但是大到一定程度就不行了 要想追加数据,要在低级流后面加true,高级流后面加不了 不是乱码,不是让人看的 保持数据一一对应 否则会报错 下载后,拷贝到一个包里,再 comment是你想添加的注释 txt文本也可

[算法训练营] 贪心算法专题(二)

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的…

Day02_《MySQL索引与性能优化》

文章目录 一、SQL执行顺序二、索引简介1、关于索引2、索引的类型Btree 索引Btree 索引 三、Explain简介四、Explain 详解1、id2、select_type3、table4、type5、possible_keys6、key7、key_len8、ref9、rows10、Extra11、小案例 五、索引优化1、单表索引优化2、两表索引优化3、…

RT-DETR算法优化改进:一种新颖的动态稀疏注意力(BiLevelRoutingAttention) | CVPR2023

💡💡💡本文独家改进: 提出了一种新颖的动态稀疏注意力(BiLevelRoutingAttention),以实现更灵活的计算分配和内容感知,使其具备动态的查询感知稀疏性 1)代替RepC3进行使用; 2)BiLevelRoutingAttention直接作为注意力进行使用; 推荐指数:五星 RT-DETR魔术师专栏介…

leetcode刷题日记:118.Pascal‘s Triangle(杨辉三角)

118.Pascal’s Triangle(杨辉三角&#xff09; 题目给我们一个整数numRows表示杨辉三角形的行数&#xff0c;返回杨辉三角形的前numRows行&#xff0c;下面给出一个杨辉三角形看看它有哪些规律&#xff1b; 可以看出杨辉三角形的每一行的最左侧和最右侧的值都为1. 其余的第…

Marin说PCB之 PCB封装和原理图封装的藕断丝连

最近天气开始降温了&#xff0c;小编我不得不拿出珍藏多年的秋裤穿上了&#xff0c;就是走路不太方便&#xff0c;有点紧啊&#xff0c;可能是当时衣服尺码买小了吧&#xff0c;不可能是我吃胖了&#xff0c;这个绝对不可能。 话说小编我今年属实有点走霉运啊&#xff0c;下班和…

虚拟仪器软件结构VISA

1、什么是VISA VISA是虚拟仪器软件结构(Virtual Instrument Software Architectuere)的简称&#xff0c;是由VXI plug & play系统联盟所统一制定的I/O接口软件标准及其相关规范的总称。一般称这个I/O函数库为VISA库&#xff08;用于仪器编程的标准I/O函数库&#xff09;。…

Allegro层叠中的Etch Factor-铜皮的腐蚀因子如何计算

Allegro层叠中的Etch Factor-铜皮的腐蚀因子如何计算 在用Allegro进行PCB设计的时候,Cross-section中需要填入对应的信息,一般填入每层的厚度即可,如下图 当PCB需要进行仿真分析的时候,Etch-Factor这个值是必须要填写的,如下图 目前看到的都是90这个值,这是一个理论值。 …

app软件开发多少钱?功能会影响价格吗?

随着智能手机的普及&#xff0c;app开发市场日益繁荣&#xff0c;很多人都有开发app的梦想&#xff0c;但开发一款app需要多少钱呢?功能是否会影响价格?本文将为你揭开这个谜团。 一、app开发费用的影响因素 app开发费用受到多种因素的影响&#xff0c;例如开发难度、功能复…

Mysql Explain工具介绍

使用EXPLAIN关键字可以模拟优化器执行SQL语句&#xff0c;分析查询语句或是结构的性能瓶颈。 准备表 -- 课程表 CREATE TABLE class (id int(11) NOT NULL,name varchar(45) DEFAULT NULL,update_time datetime DEFAULT NULL,PRIMARY KEY (id)) ENGINEInnoDB DEFAULT CHARSET…

通过流量分析查看业务系统运行和访问情况

在当今数字化时代&#xff0c;应用程序的运行和访问情况对于企业和组织来说至关重要。无论是在线销售平台、移动应用还是企业内部系统&#xff0c;应用的性能和可用性直接影响着用户体验、业务流程以及组织效率。因此&#xff0c;对应用的运行和访问情况进行全面分析和评估&…

【01】Istio-1.17 部署

1.1 部署Istio控制平面 部署方法 istioctl istio的专用管理工具&#xff0c;支持定制控制平面和数据平面通过命令行的选项支持完整的IstioOperator API命令行各选项可用于单独设置&#xff0c;以及接收包含IstioOperator自定义资源(CR)的yaml文件 Istio Operator Istio相关的自…

MSSQL 配置ORACLE ​链接服务器

在有些场景&#xff0c;我们需要整合其他异构数据库的数据。我们可以使用代码去读取&#xff0c;经过处理后&#xff0c;再将数据保存到MSSQL数据库中。如果数据量比较大&#xff0c;但处理的逻辑并不复杂的情况下&#xff0c;这种方式就不是最好的办法。这时可以使用使用链接服…

笔尖笔帽检测1:笔尖笔帽检测数据集(含下载链接)

笔尖笔帽检测1&#xff1a;笔尖笔帽检测数据集(含下载链接) 目录 笔尖笔帽检测1&#xff1a;笔尖笔帽检测数据集(含下载链接) 1. 前言 2. 手笔检测数据集 &#xff08;1&#xff09;Hand-voc1 &#xff08;2&#xff09;Hand-voc2 &#xff08;3&#xff09;Hand-voc3 …