【Spring】聊聊Spring如何解决的循环依赖以及三级缓存

news2024/11/13 14:53:28

循环依赖是什么

在平时的面试中,只要问到Spring,那么大概率肯定会问什么是循环依赖,Sping是如何解决循环依赖的。以及三级缓存机制是什么。所以为了从根本上解决这个问题,本篇主要详细介绍一下循环依赖的问题。

Spring Bean初始化过程

Spring 在创建Bean对象的时候,会按照一定的流程进行初始化。主要的话就是如下

  • 根据配置文件,扫描固定的路径 比如bean.xml ,封装成BeanDefinition
  • 根据BeanDefinition 推断出Class构造方法,反射得到一个原始对象。
  • 对原始对象进行属性复制。以及对应的切面处理等。生产代理对象放入BeanFactory中。

循环依赖其实就是A、B两个类,A同时引用B,B同时引用A。
A初始化的时候,需要引用到B,然后会去创建B,但是B又引用到了A。所以就会出现循环依赖。
一句话就是:循环依赖是在SpringBean初始化声明周期而产生的问题
在这里插入图片描述

有哪些方式的循环依赖

我们知道Bean的属性注入,1.通过构造注入 2.通过setter方式注入。
并且因为Bean scope=“singleton” 、prototype 两种方式。所以就有三种方式。

  • 1.scope = prototype 构造注入 (直接报错)
  • 2.score = singleton 构造注入 (直接报错)
  • 3.scope = singleton setteer注入 (三级缓存解决)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'serviceB' defined in class path resource [beans.xml]: Cannot resolve reference to bean 'serviceA' while setting bean property 'serviceA'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'serviceA': Requested bean is currently in creation: Is there an unresolvable circular reference?

好了,来分析以下,scope = prototype的方式。对象是多例,无法解决。并且构造对象这种方式,就是鸡生蛋,蛋生鸡。无法解决。

所以大家常说的spring 循环依赖问题,默认就是单例下 使用setter方式。

Code

@Service
public class ServiceA {
	private ServiceB serviceB;

	public ServiceB getServiceB() {
		return serviceB;
	}

	public void setServiceB(ServiceB serviceB) {
		this.serviceB = serviceB;
	}

	public ServiceA() {
		System.out.println("serviceA构造成功");
	}
}
@Service
public class ServiceB {

	private ServiceA serviceA;

	public ServiceA getServiceA() {
		return serviceA;
	}

	public void setServiceA(ServiceA serviceA) {
		this.serviceA = serviceA;
	}

	public ServiceB() {
		System.out.println("serviceB构造成功");
	}
}
	<bean id="serviceA" class="com.qxlx.main.springbean.circulardend.ServiceA" scope="singleton">
	<property name="serviceB" ref="serviceB"/>
	</bean>


	<bean id="serviceB" class="com.qxlx.main.springbean.circulardend.ServiceB"  scope="singleton">
	<property name="serviceA" ref="serviceA"/>
	</bean>

三级缓存源码解读

三级缓存其实就是DefaultSingletonBeanRegistry中的三个Map。

实例化:创建了对象,但是还有属性的赋值。 new A()
初始化:将对象需要的属性赋值 a.setB(b);

三级缓存

  /** 享元模式的单例。缓存所有单实例对象,单例对象池。ioc容器-单例池; Cache of singleton objects: bean name to bean       instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** Cache of singleton factories: bean name to ObjectFactory. */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/** Cache of early singleton objects: bean name to bean instance. */
	private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

第一级缓存〈也叫单例池)singletonObjects:存放已经经历了完整生命周期的Bean对象
第二级缓存: earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完整)
第三级缓存: Map<<String, ObiectFactory<<?>> singletonFactories,存放可以生成Bean的工厂

4大方法

在这里插入图片描述

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

整体流程

在这里插入图片描述
流程总结
在这里插入图片描述
在这里插入图片描述
整体流程,其实就是A创建的过程中需要B,所以将A对象自己放入三级缓存中,然后去实例化B。
2.B在实例化的时候,发现自己引用了属性A,所以从三级缓存中依此查询,查询一级缓存没有,在查询二级缓存也没有,发现三级缓存有,将A从三级缓存放入二级缓存,并将三级缓存中的A删除。
3.B创建完毕之后,将自己放入一级缓存中。然后A接着创建,直接从一级缓存中获取B。A创建完成,将自己放入一级缓存中。
整个过程其实就是依赖于如果发现是循环依赖的话,通过将对象提前暴露出来,存储缓存中,并且scope=singleton。所以可以解决这个问题。

小结

本篇主要介绍了Spring循环依赖的。为什么会有循环依赖的问题,以及循环依赖有哪些方式,Spring是如何通过三级缓存解决的循环依赖。

参考资料:
https://www.cnblogs.com/java-chen-hao/p/11139887.html
https://blog.csdn.net/qq_25448409/article/details/111247655?spm=1001.2014.3001.5502
https://blog.csdn.net/a745233700/article/details/110914620

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

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

相关文章

IDEA格式化代码快捷键Ctrl+Alt+L失效解决

常见问题是网易云全局快捷键冲突 解决方法&#xff1a;取消下面的全局快捷键勾选

查看进程方式

目录 ps top uptime pstree ps 查看静态的进程统计信息 top 实时显示系统中各个进程的资源占用情况 第一行 top - 17:00:23 up 15 min, 1 user, load average: 1.05, 1.22, 0.98 17:00:23————当前时间 up 15 min————系统运行时间 1 user————当前登录用户数…

客户端实现阿里云OSS文件上传(分片上传,断点续传)

前言 阿里云OSS&#xff08;Object Storage Service&#xff09;是一种稳定、安全、高扩展性的云存储服务&#xff0c;它允许您以低成本、高可靠、高可用的方式存储和访问任意类型的数据。在实际应用中&#xff0c;文件上传是一个常见的功能需求。为了提高上传效率和文件完整性…

Leetcode-每日一题【剑指 Offer 56 - I. 数组中数字出现的次数】

题目 一个整型数组 nums 里除两个数字之外&#xff0c;其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n)&#xff0c;空间复杂度是O(1)。 示例 1&#xff1a; 输入&#xff1a;nums [4,1,4,6]输出&#xff1a;[1,6] 或 [6,1] 示例 2&#x…

IOS开发:去除TabView的底部留白

我最近在做IOS开发的时候&#xff0c;使用SwiftUI中的TabView做左右滚动的页面切换&#xff0c;遇到了页面底部有大量留白无法去除的问题&#xff1a; 我查了很多资料都没有看到网上有人记录这个问题的解决方案&#xff0c;后来查阅apple developer的文档&#xff0c;我发现.ed…

模电基础知识学习笔记

文章目录&#xff1a; 一&#xff1a;基本元器件介绍 1.二极管 1.1 普通二极管特性测试 1.2 稳压二极管测试 1.3 整流二极管 1.4 开关二极管 2.电容 3.三极管(电流控制) 3.1 介绍 3.2 类型&#xff08;PNP、NPN&#xff09; 3.3 三种工作状态:放大状态、截止状态…

RBAC权限详解

1.传统的权限设计 首先&#xff0c;我们先了解下什么是传统的权限设计 从上面的图中&#xff0c;我们发现&#xff0c;传统的权限设计是对每个人进行单独的权限设置&#xff0c;但这种方式已经不适合目前企业的高效管控权限的发展需求&#xff0c;因为每个人都要单独去设置权限…

大盗阿福(记忆化搜索板子)

提供核心代码&#xff1a;&#xff08;经典的记忆化搜索套路&#xff09; int dfs(int pos){if(f[pos]!-1) return f[pos];//记忆化if(pos>n) return 0;//边界&#xff0c;越界int sum0;//模板int f10,f20;f1dfs(pos1);f2dfs(pos2)w[pos];summax(f1,f2);//模板f[pos]sum;//模…

表达式树

请设计一个算法&#xff0c;将给定的表达式树(二叉树)转换为等价的中缀表达式(通过括号反映操作符的计算次序)并输出。 例如&#xff0c;当下列两棵表达式树作为算法的输入时&#xff1a; 输出的等价中缀表达式分别为 (ab)*(c*(-d)) 和 (a*b)(-(c-d))。 注意&#xff1a; 树…

ChatGPT炒股:自动批量提取股票公告中的表格并合并数据

在很多个股票公告中&#xff0c;都有同样格式的“日常性关联交易”的表格&#xff0c;如何合并到一张Excel表格中呢&#xff1f; 首先&#xff0c;在ChatGPT中输入提示词&#xff1a; 写一段Python代码&#xff1a; F盘文件夹“新三板 2023年日常性关联交易20230704”中很多…

零代码编程:PDF文件名和Excel数据进行比对找不同

F盘“北交所招股说明书”文件夹下有150个文件&#xff1b; F盘”北证A股20230703.xlsx”表格中证券名称有200多个&#xff1b; 现在想找出文件夹下的哪些证券名称不在表格里面。 在ChatGPT中输入提示词&#xff1a; 写一段Python程序&#xff1a; F盘“北交所招股说明书”文…

qt源码--事件系统之QAbstractEventDispatcher

1、QAbstractEventDispatcher内容较少&#xff0c;其主要是定义了一些注册接口&#xff0c;如定时器事件、socket事件、注册本地事件、自定义事件等等。其源码如下&#xff1a; 其主要定义了大量的纯虚函数&#xff0c;具体的实现会根据不同的系统平台&#xff0c;实现对应的方…

AD21原理图的高级应用(五)自定义原理图模板及调用

&#xff08;五&#xff09;自定义原理图模板及调用 1.创建原理图模板2.调用原理图模板 1.创建原理图模板 利用 Altium Designer 软件在原理图中创建自己的模板,可以在图纸的右下角绘制一个表格用于显示图纸的一些参数,例如文件名、作者、修改时间、审核者、公司信息、图纸总数…

建造者设计模式 + 高阶函数 => DSL

该设计模式适用于创建复杂对象&#xff0c;该复杂对象通常是由各个部分的子对象用一定的算法或者步骤构成&#xff0c;针对每个子对象内部算法和步骤通常是稳定的&#xff0c;但是该复杂对象的确实由于不同的需求而选择使用不同的子对象进行组装。对于构建该复杂的对象&#xf…

Vue2 第六节 key的作用与原理

&#xff08;1&#xff09;虚拟DOM &#xff08;2&#xff09;v-for中的key的作用 一.虚拟DOM 1.虚拟DOM就是内存中的数据 2.原生的JS没有虚拟DOM: 如果新的数据和原来的数据有重复数据&#xff0c;不会在原来的基础上新加数据&#xff0c;而是重新生成一份 3. Vue会有虚拟…

结构方程模型的绘制

模型的绘制是我们最终呈现出的一个结果 所以说这个课程主要关注的有两点 第一点就是模型图的绘制 第二点就是结果的解释 关于中间计算过程和背后的理论的一个结果 在本文章的所有的讲解过程中 只注重模型图的绘制方法 如何高效的绘制出所需要的一个模型图 同时能够调整…

【小吉带你学Git】Git分支

&#x1f38a;专栏【Git】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【Counting Stars 】 欢迎并且感谢大家指出小吉的问题&#x1f970; 文章目录 &#x1f916;概述&#x1f354;什么是分支&#x1f354;使用分支的好处&am…

百万QPS系统如何设计?

一、关系链业务简介 从主站业务角度来看&#xff0c;关系链指的是用户A与用户B的关注关系。以关注属性细分&#xff0c;以关注&#xff08;订阅&#xff09;为主&#xff0c;还涉及拉黑、悄悄关注、互相关注、特别关注等多种属性或状态。目前主站关系链量级较大&#xff0c;且还…

九、HAL_IWDG独立看门狗的使用

1、开发环境 (1)Keil MDK: V5.38.0.0 (2)STM32CubeMX: V6.8.1 (3)MCU: STM32F407ZGT6 2、IWDG简介 (1)IWDG即独立看门狗。 (2)看门狗本质上是一个定时器&#xff0c;设置一个时间&#xff0c;时间到即让程序复位。所以需要在在时间未到之前重置定时器&#xff0c;也就是喂…

线性表详细讲解

2.1 线性表的定义和特点2.2 案例引入2.3 线程表的类型定义2.4 线性表的顺序表示和实现2.4.1 线性表的顺序存储表示2.4.2 线性表的结构类型定义2.4.3 顺序表基本操作的实现2.4.4 顺序表总结 2.5 线性表的链式表示和实现2.5.1 线性表的链式存储表示2.5.2 单链表的实现&#xff08…