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

循环依赖是什么

在平时的面试中,只要问到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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

谷粒商城第七天-商品服务之分类管理下的分类的拖拽功能的实现

目录 一、总述 1.1 前端思路 1.2 后端思路 二、前端实现 2.1 判断是否能进行拖拽 2.2 收集受影响的节点&#xff0c;提交给服务器 三、后端实现 四、总结 一、总述 这个拖拽功能对于这种树形的列表&#xff0c;整体的搬迁是很方便的。但是其实现却并不是那么的简单。 …

Android SDK 上手指南||第一章 环境需求||第二章 IDE:Eclipse速览

第一章 环境需求 这是我们系列教程的第一篇&#xff0c;让我们来安装Android的开发环境并且把Android SDK运行起来&#xff01; 介绍 欢迎来到Android SDK入门指南系列文章&#xff0c;如果你想开始开发Android App&#xff0c;这个系列将从头开始教你所须的技能。我们假定你…

Vue2封装自定义全局Loading组件

前言 在开发的过程中&#xff0c;点击提交按钮&#xff0c;或者是一些其它场景总会遇到Loading加载框&#xff0c;PC的一些UI库也没有这样的加载框&#xff0c;无法满足业务需求&#xff0c;因此可以自己自定义一个&#xff0c;实现过程如下。 效果图 如何封装&#xff1f; 第…

MySQL | 常用命令示例

MySQL | 常用命令示例 一、启停MySQL数据库服务二、连接MySQL数据库三、创建和管理数据库四、创建和管理数据表五、数据备份和恢复六、查询与优化 MySQL是一款常用的关系型数据库管理系统&#xff0c;广泛应用于各个领域。在使用MySQL时&#xff0c;我们经常需要编写一些常用脚…

Qt之切换语言的方法(传统数组法与Qt语言家)

http://t.csdn.cn/BVigB 传统数组法&#xff1a; 定义一个字符串二维数组&#xff0c; QString weekStr[2][7] {"星期一","星期二","星期三","星期四","星期五","星期六","星期日",\ "Monday&…

自己创建的类,其他类中使用错误

说明&#xff1a;自己创建的类&#xff0c;在其他类中创建&#xff0c;报下面的错误&#xff08;Cannot resolve sysmbol ‘Redishandler’&#xff09;&#xff1b; 解决&#xff1a;看下是不是漏掉了包名 加上包名&#xff0c;问题解决&#xff1b;

云计算——云计算与虚拟化的关系

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​ 目录 前言 一.虚拟化 1.什么是虚拟化 2.虚拟化技术作用 二.云计算与虚拟化的关系 三.虚…

[STL]stack和queue使用介绍

[STL]stack和queue使用介绍 文章目录 [STL]stack和queue使用介绍stack使用介绍stack介绍构造函数empty函数push函数top函数size函数pop函数 queue使用介绍queue介绍构造函数empty函数push函数front函数back函数size函数pop函数 deque介绍 stack使用介绍 stack介绍 stack是一种…

Tensorflow报错protobuf requires Python ‘>=3.7‘ but the running Python is 3.6.8

报错信息 仔细观察下方命令后&#xff0c;可得运行:python -m pip install --upgrade pip即可 完成后再次执行性安装命令 成功&#xff01;&#xff01;&#xff01;

爆肝!《Java 权威面试指南(阿里版)》,冲击“金九银十”有望了

这次金九银十你准备好了吗&#xff1f; 莫慌莫慌&#xff0c;“面试造火箭&#xff0c;工作拧螺丝” 说得不无道理&#xff0c;偶然从朋友那得到的这份 Alibaba 内部疯传《Java 权威面试指南&#xff08;阿里版&#xff09;》堪称精品&#xff0c;或可能助你一臂之力&#xff…

Doris注意事项,Doris部署在阿里云,写不进去数据

1.Doris官网 Doris官网https://doris.apache.org/ 2.根本原因 本地idea访问FE&#xff0c;FE会返回BE的地址&#xff0c;但是在服务器上通过ip addr查看&#xff0c;发现只有局域网IP&#xff0c;所以FE返回了局域网的IP&#xff0c;导致idea连接不上BE 3.解决办法 重写Ba…

Jmeter并发测试

基本步骤 1、新建线程组 测试计划右键——>添加——>线程&#xff08;用户&#xff09;——>线程组 2、 添加HTTP请求 线程组右键——>添加——>取样器——>HTTP请求 3、 添加HTTP信息头管理器 线程组右键——>添加——>配置元件——>HTTP信息头…

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

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

2023 7-30

题目1 lee2331.计算布尔二叉树的值 对于一棵完整的二叉树(每一个根节点孩子的个数不是0就是2) 叶子节点是1或者是0,其中1代表true,0代表false非叶子节点的值是2或者3,其中2代表逻辑或or,3代表逻辑与and计算方式 如果节点是个叶子节点,那么节点的 值 为它本身,即 True 或者…

一、创建自己的docker python容器环境;支持新增python包并更新容器;离线打包、加载image

1、创建自己的docker python容器环境 参考&#xff1a;https://blog.csdn.net/weixin_42357472/article/details/118991485 首先写Dockfile&#xff0c;注意不要有txt等后缀 Dockfile # 使用 Python 3.9 镜像作为基础 FROM python:3.9# 设置工作目录 WORKDIR /app# 复制当前…

创造自己的宠物医院预约服务小程序,步骤详解

在现代社会&#xff0c;越来越多的人开始养宠物&#xff0c;而宠物的健康管理也成为了一个重要的话题。为了方便宠物主人随时随地进行宠物医院的管理和服务&#xff0c;开发一个宠物医院管理小程序是很有必要的。今天我们将分享一些制作宠物医院管理小程序的技巧&#xff0c;帮…

基于Open3D的点云处理12-体素化

体素化Voxelization 体素&#xff08;voxel&#xff09;是像素&#xff08;pixel&#xff09;、体积&#xff08;volume&#xff09;和元素&#xff08;element&#xff09;的组合词&#xff0c;相当于3D空间中的像素; 体素化是通过用空间均匀大小的体素网格(voxel grid)来模…

【C++】开源:grpc远程过程调用(RPC)配置与使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍grpc远程过程调用&#xff08;RPC&#xff09;配置与使用。 无专精则不能成&#xff0c;无涉猎则不能通。。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜…

【编程规范】一文讲解开发中的命名规范

命名规范 好的代码本身就是注释, 所以我们需要统一命名风格。 ​ 在本文中&#xff0c;将从大到小&#xff0c;从外到内&#xff0c;总结Java编程中的命名规范。文中将会涉及到日常工作中常见的命名示例&#xff0c;如包命名&#xff0c;类命名&#xff0c;接口命名&#xff0c…

页面生成图片或PDF node-egg

没有特别的幸运&#xff0c;那么就特别的努力&#xff01;&#xff01;&#xff01; 中间件&#xff1a;页面生成图片 node-egg 涉及到技术node egg Puppeteer 解决文书智能生成多样化先看效果环境准备初始化项目 目录结构核心代码 完整代码https://gitee.com/hammer1010_ad…