Spring循环依赖解决方案
解决方案
使用提前暴露机制+三级缓存进行解决
| singletonObjects | 一级缓存,存放完整的 Bean。 |
| earlySingletonObjects | 二级缓存,存放提前暴露的Bean,Bean 是不完整的,未完成属性注入和执行 init 方法。 |
| singletonFactories | 三级缓存(用Map类型的该参数来存放beanName和beanFactory之间的关系),存放的是 Bean 工厂(通过BeanFactory的getObject拿到实例,然后重新将值赋给二级缓存)。 |
这里只用 A,B 形成的循环依赖来举例:
- 实例化 A,此时 A 还未完成属性填充和初始化方法(@PostConstruct)的执行,A 只是一个半成品。
- 为 A 创建一个 Bean 工厂,并放入到 singletonFactories 中。
- 发现 A 需要注入 B 对象,但是一级、二级、三级缓存均为发现对象 B。
- 实例化 B,此时 B 还未完成属性填充和初始化方法(@PostConstruct)的执行,B 只是一个半成品。
- 为 B 创建一个 Bean 工厂,并放入到 singletonFactories 中。
- 发现 B 需要注入 A 对象,此时在一级、二级未发现对象 A,但是在三级缓存中发现了对象 A,从三级缓存中得到对象 A(通过工厂生成半成品Bean,提前暴露),并将对象 A 放入二级缓存中,同时删除三级缓存中的对象 A。(注意,此时的 A 还是一个半成品,并没有完成属性填充和执行初始化方法)
- 将对象 A 注入到对象 B 中。
- 对象 B 完成属性填充,执行初始化方法,并放入到一级缓存中,同时删除二级缓存中的对象 B。(此时对象 B 已经是一个成品)
- 对象 A 得到对象 B,将对象 B 注入到对象 A 中。(对象 A 得到的是一个完整的对象 B)
- 对象 A 完成属性填充,执行初始化方法,并放入到一级缓存中,同时删除二级缓存中的对象 A。
思考1:
只使用一级缓存,是否可以解决循环依赖
答案是否定的,Bean的创建过程 createBeanInstance --> populateBean --> initializeBean,可以推断出是在initializeBean才插入一级缓存的。
由上面推断 initializeBean 的时候记录缓存,在循环依赖的情况,需要在 populateBean(第二阶段) 的时候再去注入循环依赖的 bean,此时,缓存中是没有循环依赖的 bean 的,就会导致 bean 重新创建实例。
如果被循环依赖的 bean 是一个 AOP 增强的代理 bean 的话,bean 的原始引用和最终产生的 AOP 增强 bean 的引用是不一样的,一级缓存就搞不定了。
当 aop 代理 bean 被循环依赖时,getBean() 操作从缓存中获取到的是一级缓存中存放的原始对象,而不是代理对象。
- 假如createBeanInstance 之后就生成代理对象提前放入一级缓存呢?
答案是否定的,如果在 populateBean 之前生成的是一个代理对象的话,会导致一个问题 :jdk proxy 产生的代理对象是实现的目标类的接口,jdk proxy 的代理类通过 BeanWrapper 去利用反射设置值时会因为找不到相应的属性或者方法而报错。
思考2:
不需要第三级缓存,是否可以解决循环依赖?
答案是可以的,二级缓存来解决循环依赖的话,那么每个 bean 的创建流程中都需要插入一个流程——创建 bean 的早期引用(提前暴露)放入二级缓存。其实,在真实的开发中,绝大部分的情况下都不涉及到循环依赖而且 createBeanInstance --> populateBean --> initializeBean 这个流程也更加符合常理。所以,猜想 Spring 不用二级缓存来解决循环依赖问题,是为了保证处理时清晰明了,bean 的创建就是三个阶段: createBeanInstance --> populateBean --> initializeBean
只有碰到 AOP 代理 bean 被循环依赖时的场景,才去特殊处理,提前生成 AOP 代理 bean
思考3:
去掉二级缓存,是否可以解决循环依赖?
答案是否定的,在AOP的前提下,这么一种场景,B 和 C 都依赖了 A。singletonFactory.getObject() 获取的是代理对象。多次调用该方法返回的代理对象是不同的。
思考4:
Spring 一开始提前暴露的并不是实例化的 Bean,而是将 Bean 包装起来的 ObjectFactory。为什么要这么做呢?
这实际上涉及到 AOP,如果创建的 Bean 是有代理的,那么注入的就应该是代理 Bean,而不是原始的 Bean。但是 Spring 一开始并不知道 Bean 是否会有循环依赖,通常情况下(没有循环依赖的情况下),Spring 都会在完成填充属性,并且执行完初始化方法之后再为其创建代理。但是,如果出现了循环依赖的话,Spring 就不得不为其提前创建代理对象,否则注入的就是一个原始对象,而不是代理对象。Spring 的做法就是在 ObjectFactory 中去提前创建代理对象。
Spring 需要三级缓存的目的是为了在没有循环依赖的情况下,延迟代理对象的创建
相关文章:
Spring循环依赖解决方案
解决方案 使用提前暴露机制三级缓存进行解决 singletonObjects一级缓存,存放完整的 Bean。earlySingletonObjects二级缓存,存放提前暴露的Bean,Bean 是不完整的,未完成属性注入和执行 init 方法。singletonFactories三级缓存(用…...
解决 IntelliJ IDEA 运行时 “Command line is too long“ 问题
文章目录 文章标题:解决 IntelliJ IDEA 运行时 "Command line is too long" 问题简介问题描述解决方案代码示例代码示例1:使用JAR Manifest代码示例2:使用Classpath File代码示例3:优化项目依赖 结论进一步的资源 文章标…...
鸿蒙网络编程系列5-TCP连接超时分析
1. TCP连接超时简介 TCP是面向连接的协议,通过三次握手建立连接,但是,在建立连接的过程中对方有可能没有响应,这时候发起连接的一方会重试,如果重试多次仍然没有响应,就会触发超时,从而导致连接…...
金蝶云星空移动字段后关闭页面后重新打开无效
有同事反馈,单据的明细字段里面移动了字段,然后退出,其他字段都能按最后排版的位置显示,有个别字段始终无法按照排版的位置显示。 只需要打开BOS平台,找到对应字段,然后更改可见性。...
幂律分布笔记
一、幂律分布的数据拟合 数据分箱: 所谓分箱就是对原始数据进行分组,然后对每一组内的数据进行平滑处理。常见的分箱方式主要有等深分箱、等宽分箱、用户自定义等 对数分箱: 对原数据进行分箱,第i个箱的宽度为bi,b…...
一些NLP代表性模型
(一)BERT 由Bidirectional Encoder Representations from Transformers的首字母组成,是encoder-only结构类型的代表。 模型分预训练和微调两步,预训练任务有两类:masked language model(MLM)、next sentence predict…...
低代码移动端开发:未来的趋势与挑战
什么是低代码移动端开发? 低代码移动端开发平台允许开发者通过可视化界面和少量编码来构建应用程序。相较于传统的代码开发,低代码平台大大降低了技术和学习门槛,使非专业开发人员也能参与到移动应用的开发过程中。 低代码移动端开发的优势 …...
【Linux】嵌入式Linux系统的组成、u-boot编译
Linux—嵌入式Linux系统的组成、u-boot编译 前言一、嵌入式Linux系统的组成1.1 嵌入式Linux系统和PC完整的操作系统的对比如下:1.2 PC机—Windows系统启动流程(PC机—Linux系统、嵌入式ARM—linux系统的启动流程类似) 二、编译u-boot2.1 u-bo…...
Qt打开excel文件,并读取指定单元格数据
1. 下载并安装QXlsx库,详见之前的博文Qt子线程创建excel文件报错QObject: Cannot create children for a parent that is in a different thread.-CSDN博客 2. // 创建一个XlsxDocument对象QString filename "D:\\mydocuments\\data_acquisition\\data\\tes…...
适合下班回家做的小副业,用AI做视频,几天时间3000+
大家好,今天要给大家分享的项目是定制儿歌,精准定位宝妈群体,每天轻松赚500! ***01* 项目原理 父母都非常疼爱自己的孩子,愿意为孩子提供独特的东西。而我们正是利用这一点,通过免费AI工具生成专属的儿童…...
git的基本操作 + 分支管理
一、基本操作 1. 修改文件 Git比其他的版本管理器设计得更加优秀,因为Git追踪并管理的是修改,而非文件。 修改一个文件,不管你是添加一行,或者删除一行,还是添加了又删除了,甚至你创建了一个新文件&…...
VRRP
1、VRRP简介 虚拟路由冗余协议 VRRP(Virtual Router Redundancy Protocol)通过把几台路由设备联合组成一台虚拟的路由设备,将虚拟路由设备的IP地址作为用户的默认网关实现与外部网络通信。当网关设备发生故障时(单点故障…...
个人健康系统|个人健康数据管理系统|基于小程序+java的个人健康数据管理系统设计与实现(源码+数据库+文档)
个人健康数据管理系统 目录 基于小程序java的个人健康数据管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍:✌️大厂码农|毕设布道师…...
R语言统计分析——折线图
参考资料:R语言实战【第2版】 如果将散点图上的点从左到右连接起来,就会得到一个折线图。以基础安装中的Orange数据集为例,展示如下: # 设置绘图参数 opar<-par(no.readonly TRUE) # 画布拆分为1行2列 par(mfrowc(1,2)) # 选…...
前端怎么实现电子签名
电子签名(e-signature)作为一种数字化的签署方式,广泛应用于合同、协议等文件的确认中。随着科技的发展,前端技术也为电子签名的实现提供了便利。本文将探讨在前端如何实现电子签名,包括技术选型、实现步骤及注意事项。…...
数字后端零基础入门系列 | Innovus零基础LAB学习Day1
一 Floorplan 数字IC后端设计如何从零基础快速入门?(内附数字IC后端学习视频) Lab5-1这个lab学习目标很明确——启动Innovus工具并完成设计的导入。 在进入lab之前,我们需要进入我们的FPR工作目录。 其中ic062为个人服务器账户。比如你端…...
鼠标移入盒子,盒子跟随鼠标移动
demo效果: 鼠标移入盒子,按下鼠标,开启移动跟随移动模式,再次按下关闭移动模式 涉及主要属性 在元素上单击鼠标按钮时输出鼠标指针的坐标: var x event.pageX; // 获取水平坐标 var y event.pageY; // 获取垂直坐标元素offsetL…...
css的简单问题
1.display:none;和visibility:hidden;的区别 相同点:都可以让元素不可见 区别: display:none;可以让元素完成在渲染树中消失,渲染时不占任何空间;visibility:hidden;不会让元素从渲染树消失,渲染元素继续占据空间&a…...
使⽤ Override 和 New 关键字进⾏版本控制(C#)
文章目录 1. 基础概念1.1 override1.2 new 2. 示例代码1. override 关键字2. new 关键字 3.完整示例测试3.1 基类和派生类的定义3.2 测试代码3.3 运行结果 结论 在 C# 中,override 和 new 关键字用于控制类之间的成员方法的隐藏和重写。理解它们之间的差异和使用场景…...
JavaScript 15章:模块化编程
在现代软件开发中,模块化编程是一种非常重要的实践,它可以帮助开发者组织代码,提高代码的复用性和可维护性。以下是关于模块化编程的一些关键知识点和实战案例: 第15章:模块化编程 模块的概念 模块是指将一组相关的…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...
基于Java+MySQL实现(GUI)客户管理系统
客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息,对客户进行统一管理,可以把所有客户信息录入系统,进行维护和统计功能。可通过文件的方式保存相关录入数据,对…...
