来聊聊Spring的循环依赖

news2024/9/22 13:22:46

文章目录

  • 首先了解一下什么是循环依赖
  • 简述解决循环依赖全过程
    • 通过debug了解Spring解决循环依赖全过程
    • Aservice的创建
    • 递归来到Bservice的创建
    • 然后BService递归回到了getAservice的doGetBean中
    • 故事再次回到Aservice填充BService的步骤
  • 总结成流程图
  • 为什么二级就能解决循环依赖问题,而我们却要用三级缓存解决循环依赖问题呢
  • 循环依赖思想在生活中的运用

首先了解一下什么是循环依赖

如下代码所示,我们编写了一个A对象,A对象在new的时候要new一个B对象。
而我们new的B时,它会去new一个A对象,两者new的时候互相依赖,来来回回,就造成了大名鼎鼎的循环依赖问题。

public class ABTest {

	public static void main(String[] args) {
		new ClazzA();
	}

}

class ClazzA {

	private ClazzB b = new ClazzB();

}

class ClazzB {

	private ClazzA a = new ClazzA();

}

简述解决循环依赖全过程

在debug之前,我们不妨了解一下,要解决循环依赖,那么代码该怎么写?其实计算机设计中就有一个不错的思想。觉得顶不住的时候,加个缓存试试看。
所以循环依赖问题也是同理。

如下代码所示,Obj1和Obj2是两个互相依赖的类,我们在创建对象Obj1时,不妨在他new的时候先将其放到缓存中。然后他发现它依赖Obj2,我们递归再去new Obj2,然后Obj2填充属性的时候发现他依赖Obj1,于是先去缓存中看看有没有Obj1有的话填充上去(虽然这时候Obj1还是个半成品,但是先解决燃眉之急要紧),然后代码递归回到Obj1填充Obj2的代码段,完成Obj1属性填充。

/**
 * 循环依赖解决的示例代码
 */
public class CircleDepSolution {

	private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	private static <T> T getBean(Class<T> beanClass) throws Exception {
		//拿到这个bean的小写
		String beanName = beanClass.getSimpleName().toLowerCase();
		//去map中捞看看有没有
		if (singletonObjects.containsKey(beanName)) {
			return (T) singletonObjects.get(beanName);
		}
		//没有就自己去创建一个,并且塞到map中
		T obj = beanClass.newInstance();
		singletonObjects.put(beanName, obj);
		//然后捞出这个bean的所有成员属性
		Field[] fields = obj.getClass().getDeclaredFields();
		for (Field field : fields) {
			field.setAccessible(true);
			Class<?> filedCLass = field.getType();
			String filedName = filedCLass.getSimpleName().toLowerCase();
			//去map中捞,如果有就set,没有就递归调用一下再set
			field.set(obj, singletonObjects.containsKey(filedName) ? singletonObjects.get(filedName) : getBean(filedCLass));

		}
		//最终再返回这个bean
		return  obj;


	}

	public static void main(String[] args) throws Exception{
		System.out.println(getBean(Obj1.class).getObj2());
		System.out.println(getBean(Obj2.class).getObj1());
	}
}

这种方式虽然解决了问题,但是我们却发现这种方案的一个特点,依赖的只有set方式的情况下才能解决,因为set使得依赖对象的创建和new分为两步骤,流程可以把控,我们完全可以先搞个半成品放到缓存中给别人取

通过debug了解Spring解决循环依赖全过程

首先编写循环依赖的对象AService和BService

@Service("aService")
public class AService {
	@Autowired
	private BService bService;

	public BService getbService() {
		return bService;
	}

	public void setbService(BService bService) {
		this.bService = bService;
	}
}

@Service("bService")
public class BService {

	@Autowired
	private AService aService;

	public AService getaService() {
		return aService;
	}

	public void setaService(AService aService) {
		this.aService = aService;
	}
}

然后我们开始debug

Aservice的创建

首先我们在预实例化的方法里面看到Aservice的beanName
在这里插入图片描述然后我们回去尝试拿这个bean对象
在这里插入图片描述点入发现真正做事的doGetBean方法

在这里插入图片描述可以看到doGetBean方法,先会调用一个getSingleton的方法,我们点入这个方法debug时候发现他不过是从一级缓存(即存放完全体的bean容器)是狗有aService,若没有且这个bean正处于创建中就执行创建并返回。若不存在且也没在创建中,那么就返回空对象

在这里插入图片描述在这里插入图片描述我们接着往下走,至于看到一个创建bean的核心逻辑,它会判断这个bean是不是单例的,若是则传beanName和一个创建bean的lambda表示式到getSingleton中。

在这里插入图片描述

我们点入时发现他的核心调用singletonFactory.getObject()

在这里插入图片描述这个方法就会执行上一步传入的lambda的createBean,而createBean会调用doCreateBean
在这里插入图片描述

然后完成bean的创建
在这里插入图片描述然后再判断这个bean是否不是完全体,若不是则放到三级缓存中

在这里插入图片描述

在这里插入图片描述
放到三级缓存后在进行属性填充

在这里插入图片描述
而在调用属性填充过程中,我们发现一个和自动注入相关的bean后置处理

在这里插入图片描述可以看到他在尝试着将BService注入

在这里插入图片描述
点入就发现注入的核心逻辑

在这里插入图片描述
注入回去beanFactory拿到建议的bean,然后点入我们发现核心的do逻辑
在这里插入图片描述在这里插入图片描述点入do方法后我们发现它内部调用了一个findValue,返回了一个null

在这里插入图片描述

在这里插入图片描述

这时候他就会去解决循环依赖问题,从bean容器中寻找候选人
在这里插入图片描述
在这里插入图片描述
有一次的调用了getBean
在这里插入图片描述

递归来到Bservice的创建

可以发现我们以递归的方式回到了原点,只不过这次是替Aservice找Bservice

在这里插入图片描述
一顿和Aservice差不多的操作后又来到属性填充的环节
在这里插入图片描述
然后有一次来到解决循环依赖的环节

在这里插入图片描述

然后BService递归回到了getAservice的doGetBean中

在这里插入图片描述

这时候容器发现Aservice被放在三级容器中处于被创建中的状态

在这里插入图片描述然后他就会调用早期的存到三级缓存中的lambda搞出Aservice
在这里插入图片描述
在这里插入图片描述然后将其存到二级缓存中

在这里插入图片描述
自此我们解决Bservice循环依赖的Aservice的问题,就是将一个半成品的Aservice给Bservice先用着
在这里插入图片描述
自此Bservice成为完全体

在这里插入图片描述这时候Bservice就会被放到一级缓存中

在这里插入图片描述

故事再次回到Aservice填充BService的步骤

自此aService也可以在一级缓存中找到Bservice解决了循环依赖问题

在这里插入图片描述

总结成流程图

为什么二级就能解决循环依赖问题,而我们却要用三级缓存解决循环依赖问题呢

循环依赖思想在生活中的运用

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

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

相关文章

六个优质开源项目,让你更了解Django框架开发

Django 是一个开源的 Web 应用框架&#xff0c;由 Python 写成。采用了 MTV 的框架模式&#xff0c;即模型 M&#xff0c;视图 V 和模版 T。它最初是被用来开发 CMS 软件的&#xff0c;所以 Django 很适合用来搭建内容类网站&#xff0c;它的设计目的是使常见的 Web 开发任务变…

【Nginx】Nginx了解(基础)

文章目录 Nginx产生的原因Nginx简介Nginx的作用反向代理负载均衡策略动静分离 Nginx的Windows下的安装Linux下的安装Nginx常用命令 负载均衡功能演示 Nginx产生的原因 背景 一个公司的项目刚刚上线的时候&#xff0c;并发量小&#xff0c;用户使用的少&#xff0c;所以在低并发…

大数据技术之Shell(超级详细)

大数据技术之Shell&#xff08;超级详细&#xff09; 第1章 Shell概述 Shell 是一种脚本语言&#xff0c;用于在操作系统的命令行界面&#xff08;CLI&#xff09;下执行命令和脚本。在大数据领域&#xff0c;Shell 脚本常用于编写数据处理和分析任务的自动化脚本&#xff0c…

B站剧场播放模式2.0

文章目录 v 1.01、新版本改进1-1 去掉了冗余1-2 剧场模式增强1-3 演示视频 2、代码 v 1.0 油猴脚本-Bilibili剧场模式仿Youtube-CSDN博客 https://blog.csdn.net/qq_45020818/article/details/131751288 功能比较粗糙&#xff0c;很多细节不完善&#xff0c;代码也写的很乱。 …

地图自定义省市区合并展示数据整合

需求一&#xff1a;将省级地图下的两个市合并成一个区域&#xff0c;中间的分割线隐藏。 1、访问下方地址&#xff0c;搜索并下载省级地图json文件。 地址&#xff1a;https://datav.aliyun.com/portal/school/atlas/area_selector 2、切换到边界生成器&#xff0c;上传刚刚下…

Milesight VPN server.js 任意文件读取漏洞(CVE-2023-23907)

0x01 产品简介 MilesightVPN 是一款软件&#xff0c;一个 Milesight 产品的 VPN 通道设置过程更加完善&#xff0c;并可通过网络服务器界面连接状态。 0x02 漏洞概述 MilesightVPN server.js接口处存在文件读取漏洞&#xff0c;攻击者可通过该漏洞读取系统重要文件&#xff…

(1)(1.7) HOTT telemetry

文章目录 前言 1 布线和设置 2 参数说明 前言 Plane-4.0.0&#xff08;及更高版本&#xff09;、Copter-4.0.4&#xff08;及更高版本&#xff09;和 Rover-4.1.0&#xff08;及更高版本&#xff09;支持 Graupner HOTT 遥测技术。 1 布线和设置 与自动驾驶仪的连接可通过…

HNU-计算机网络-实验3-应用层和传输层协议分析(PacketTracer)

计算机网络 课程基础实验三应用层和传输层协议分析&#xff08;PacketTracer&#xff09; 计科210X 甘晴void 202108010XXX 【给助教的验收建议】 如果是助教&#xff0c;比起听同学读报告&#xff0c;更好的验收方式是随机抽取一个场景&#xff08;URL/HTTPS/FTP&#xff09…

Vue笔记-在axios中的than函数中使用this需要注意的地方

在Vue中&#xff0c;可以使用this关键字来访问到组件中定义的变量。然而&#xff0c;在axios的then函数中&#xff0c;this关键字的作用域会改变&#xff0c;会指向axios对象本身而不是Vue组件实例。因此&#xff0c;不能直接访问到Vue组件中定义的变量。 解决这个问题的一种方…

Java技术栈 —— 微服务框架Spring Cloud —— Ruoyi-Cloud 学习(二)

RuoYi项目开发过程 一、登录功能(鉴权模块)1.1 后端部分1.1.1 什么是JWT?1.1.2 什么是Base64?为什么需要它&#xff1f;1.1.3 SpringBoot注解解析1.1.4 依赖注入和控制反转1.1.5 什么是Restful?1.1.6 Log4j 2、Logpack、SLF4j日志框架1.1.7 如何将项目打包成指定bytecode字节…

Datawhale聪明办法学Python(task3变量与函数)

一、课程基本结构 课程开源地址&#xff1a;课程简介 - 聪明办法学 Python 第二版 章节结构&#xff1a; Chapter 0 安装 Installation Chapter 1 启航 Getting Started Chapter 2 数据类型和操作 Data Types and Operators Chapter 3 变量与函数 Variables and Functions Ch…

《算法通关村——回溯模板如何解决回溯问题》

《算法通关村——回溯模板如何解决回溯问题》 93. 复原 IP 地址 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.201" 和 "192.1…

《Effective C++》学习笔记

条款01&#xff1a;把 C 看成一个语言联邦 C由几个重要的次语言构成 C语言&#xff1a;区块&#xff0c;语句&#xff0c;预处理器&#xff0c;数组&#xff0c;指针等等。 类&#xff1a;class&#xff0c;封装&#xff0c;继承&#xff0c;多态......&#xff08;动态绑定等…

基于ssm旅游景点管理系统设计论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本旅游景点管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息…

深度学习中的预测图片中的矩形框、标签、置信度分别是什么意思。

问题描述&#xff1a;深度学习中的预测图片中的矩形框、标签、置信度分别是什么意思。 问题解答&#xff1a; 目标框&#xff08;Bounding Box&#xff09;&#xff1a; 描述目标位置的矩形边界框。 类别标签&#xff1a; 表示模型认为目标属于哪个类别&#xff08;例如&#…

证明四元数乘法与旋转矩阵乘法等价

刚体四元数姿态控制 一文中没有证明的公式 R ( Q 1 ) R ( Q 2 ) R ( Q 1 ∘ Q 2 ) R(Q_1)R(Q_2)R(Q_1\circ Q_2) R(Q1​)R(Q2​)R(Q1​∘Q2​) 在这篇文章中证明。 首先找几个数测试是否等价。 quaternions.py的代码见 自用的四元数、欧拉角、旋转矩阵转换代码。 下面的代码中…

dockerfite创建镜像---INMP+wordpress

搭建dockerfile---lnmp 在192.168.10.201 使用 Docker 构建 LNMP 环境并运行 Wordpress 网站平台 [rootdocker1 opt]# mkdir nginx mysql php [rootdocker1 opt]# ls #分别拖入四个包&#xff1a; nginx-1.22.0.tar.gz mysql-boost-5.7.20.tar.gz php-7.1.10.tar.bz2 wor…

重要通知!中国电信警告:用户须关闭路由器“双频合一”功能

在网络的无尽时空里&#xff0c;一场电信官方的宣战正酝酿中&#xff0c;目标锁定在我们日常生活中不可或缺的WiFi身上~ 最新消息曝光&#xff0c;竟然是路由器内藏的一个名为“双频合一”的功能引发了这场轰轰烈烈的网络风暴。 我们时常觉得WiFi就像是隐身在我们生活中的超级英…

Faulhaber 2.5代运动控制系统 25mNm/13W

2.5代控制系统&#xff1b; PWM输出&#xff1b; 四象限控制带&#xff1b; RS232或CANopen通信接口&#xff1b; 2250_BX4_CxD 选件&#xff0c;电缆和连接信息&#xff1a; 适配部件&#xff1a;

SystemVerilog基础:并行块fork-join、join_any、join_none(二)

相关阅读 SystemVerilog基础https://blog.csdn.net/weixin_45791458/category_12517449.html 在第一节中&#xff0c;我们讨论了并行块中的fork-join块和fork-join_any块&#xff0c;了解了它们的差异&#xff0c;本文将继续讨论fork-join_none块的使用。 fork-join_none并行块…