Spring中Bean的循环依赖
目录
定义:
循环依赖的后果:
一:三级缓存
1、大概的思路:
注意:
2、执行过程:
A半完成:
B完成:
A完成:
注:
二:@Lazy
定义:
一个或多个Bean之间存在相互调用关系就会产生循环依赖。即:A调用B,B调用C,C调用A,这样会导致创建对象时依赖链过长,栈溢出。

循环依赖的后果:
环依赖可能会导致程序出现各种问题,比如编译错误、运行时错误、死锁等。因此,避免循环依赖是编写高质量软件的重要方面之一。
Spring为了解决循环依赖问题,引入了三级缓存,Bean的生命周期中我们可以知道Bean在实例化的时候会通过Bean的构造函数来实例化Bean和进行属性填充,所以就要在这一步之前对Bean进行一些操作。
一:三级缓存
解读源码流程之前,spring 内部的三级缓存逻辑必须了解,要不然后面看代码会蒙圈。
第一级缓存 :singletonObjects,用于保存实例化、注入、初始化完成的 bean 实例;
第二级缓存 :earlySingletonObjects,用于保存实例化完成的 bean 实例;
第三级缓存 :singletonFactories,用于保存 bean 创建工厂,以便后面有机会创建代理对象。
这是最核心
执行逻辑为:
1、先从“第一级缓存”找对象a,有就返回,没有就找“二级缓存”;
2、找“二级缓存”,有就返回,没有就找“三级缓存”;
3、找“三级缓存”,a找到了,就获取对象,放到“二级缓存”,并把a从“三级缓存”移除。
下面是Bean中的三级缓存的过程源码:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {// Quick check for existing instance without full singleton lockObject singletonObject = this.singletonObjects.get(beanName);// 从一级缓存中获取// 如果一级缓存里没有,且 Bean 正在创建中// 就从二级缓存里获取if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {singletonObject = this.earlySingletonObjects.get(beanName);// 二级缓存没有,就从三级缓存获取一个工厂if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// Consistent creation of early reference within full sinsingletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 能获取到工厂则创建 BeansingletonObject = singletonFactory.getObject();// 把实例存入二级缓存this.earlySingletonObjects.put(beanName, singletonObject);// 把工厂从三级缓存移除this.singletonFactories.remove(beanName);}}}}}}return singletonObject;
}
1、大概的思路:
当我们需要创建 A 的的时候,会首先通过 Java 反射创建出来一个原始的 A,这个原始 A 可以简单理解为刚刚 new 出来还没设置任何属性的 A,此时,我们把这个 A 先存入到一个缓存池中。接下来给 A 的属性设置值和解决A A 的依赖,这时我们发现 A 依赖 B,那么就去创建 B 对象,结果创建 B 的时候,发现 B 依赖 A,那么此时就先从缓存池中取出来 A(半成品A,啥属性也没有) 先用着,然后继续 B 创建的后续流程,直到 B 创建完成后,将之赋值给 A,此时 A 和 B 就都创建完成了。
如下图:

注意:
这里拿到的半成品A是个引用对象,也就是说它是个地址,所以即使里面的属性赋值都没有做也没关系,后面运行调用时拿到的就是完整的A
2、执行过程:
A半完成:
我们在创建A的时候,会通过反射创建出原始的A,接下来检查一缓存中有没有Bean,如果没有,则首先向三级缓存中添加一条记录,记录的 key 就是当前 Bean 的 beanName,value 则是一个 Lambda 表达式 ObjectFactory,通过执行这个 Lambda 可以给当前 A 生成代理对象。然后如果二级缓存中存在当前 A Bean,则移除掉。
B完成:
这样,A的前半部分就完成了,接下来给A的各个属性赋值,赋值时发现需要B,就去创建B,B和A一样,经历到给属性赋值的阶段,发现需要A,就去一级缓存找A,找不到就去二级缓存,再去三级缓存,找到A的ObjectFactory,然后执行这里的 getObject 方法,这个方法在执行的过程中,会去判断是否需要生成一个代理对象,如果需要就生成代理对象返回,如果不需要生成代理对象,则将原始对象返回即可。
最后,把拿到手的对象存入到二级缓存中以备下次使用,同时删除掉三级缓存中对应的数据。这样 A 所依赖的 B 就创建好了。
A完成:
再去完善B,属性赋值,A也就完成了。
注:
这里的解决思路需要在有AOP的情况下才可以,这里的Bean有ObjectFactory对象,否则就只能用二级缓存来解决。
二级缓存的解决大体思路是一样的,不过要把A的半成品存放在二级缓存中,这样可以更早的被调用。
二:@Lazy
延迟加载可以通过将 bean 的依赖关系运行时进行注入,而不是在初始化阶段
@Service
public class AserviceImpl implements Aservice {@Autowired@Lazyprivate Bservice bservice;@Asyncpublic void test(){}}
Lazy 延迟加载打破循环依赖,他会通过其它途径生成bservice 的lazy 的代理对象,不会去走创建B 的代理 对象 然后注入A 这套流程。这样创建A 的单例对象并放入到单例池中,B 的bean 在实例化后,注入A bean 属性就可以从单例池中加载到A 的真正的bean ,而不会出现bean 对象不一致的问题。
相关文章:
Spring中Bean的循环依赖
目录 定义: 循环依赖的后果: 一:三级缓存 1、大概的思路: 注意: 2、执行过程: A半完成: B完成: A完成: 注: 二:Lazy 定义: …...
Java二十三种设计模式-代理模式模式(8/23)
代理模式:为对象访问提供灵活的控制 引言 代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一个代替或占位符,以控制对它的访问。 基础知识,java设计模式总体来说设计模式分为三大类&#…...
Windows 11 家庭中文版 安装 VMWare 报 安装程序检测到主机启用了Hyper-V或Device
1、问题 我的操作系统信息如下: 我在安装 VMWare 的时候,报: 因为我之前安装了 docker 桌面版,所以才报这个提示。 安装程序检测到主机启用了 Hyper-v或 Device/credential Guard。要在启用了Hyper-或 Device/Credential Guard …...
机械学习—零基础学习日志(高数09——函数图形)
零基础为了学人工智能,真的开始复习高数 函数图像,开始新的学习! 幂函数 利用函数的性质,以幂函数为例,因为单调性相同,利用图中的2和3公式,求最值问题,可以直接将式子进行简化。这…...
java迭代集合出现并发修改异常(ConcurrentModificationException)的原因以及解决方案
java迭代集合出现并发修改异常(ConcurrentModificationException)的原因以及解决方案 一. 什么时候会出现并发修改异常? 这里先看需求 : 定义一个集合,存储 唐僧,孙悟空,猪八戒,沙僧,遍历集合,如果遍历到猪八戒,往集合中添加一个白龙马 很显然要求我们先创建一个集合并进行…...
BGP选路之Local Preference
原理概述 当一台BGP路由器中存在多条去往同一目标网络的BGP路由时,BGP协议会对这些BGP路由的属性进行比较,以确定去往该目标网络的最优BGP路由。BGP首先比较的是路由信息的首选值(PrefVal),如果 PrefVal相同,就会比较本…...
WEB渗透信息收集篇--IP和端口信息
WEB渗透信息收集篇--域名信息-CSDN博客 WEB渗透信息收集篇--网站架构和指纹识别-CSDN博客 WEB渗透信息收集篇--人员信息-CSDN博客 WEB渗透信息收集篇--其他信息-CSDN博客 一、ASN ASN Tool - MxToolBox ASN通常指的是"自…...
国内微短剧系统平台抖音微信付费小程序app开发源代码交付
微短剧作为当下热门的内容,结合抖音平台的广泛用户基础,开发微短剧付费小程序APP具有显著的市场潜力,用户对于短剧内容的需求旺盛,特别是在言情、总裁、赘婿等热门题材方面,接下来给大家普及一下微短剧小程序系统。 顺…...
Java语言程序设计基础篇_编程练习题**15.19 (游戏:手眼协调)
**15.19 (游戏:手眼协调) 请编写一个程序,显示一个半径为10像素的实心圆,该圆放置在面板上的随机位置,并填充随机的顔色,如图15-29b所示。单击这个圆时,它会消失,然后在另一个随机的位置显示新的随机颜色的…...
学习记录day16—— 数据结构 双向链表 循环链表
双向链表 1、概念 1)就是从任意一个节点既能存储其前驱节点,又能存储后继节点 2)结构体中增加一个指向前驱节点的指针 //定义数据类型 typedef int datatype;//定义节点类型 typedef struct Node {union {int len;datatype data;};struct Node *prio; …...
Air780EP模块 AT开发-MQTT接入OneNET移动物联网平台应用指南
应用概述 使用AT方式通过MQTT协议连接onenet studio。官网地址:https://open.iot.10086.cn/ 材料准备 Air780EP(V)开发板一套,包括天线SIM卡,USB线。 PC电脑,串口工具 在onenet上创建产品 打开OneNET官网,进入控制…...
HOST处理器预读PCI设备
在PCI(Peripheral Component Interconnect)总线规范中,MRL(Memory Read Line)和MRM(Memory Read Multiple)是两种读取存储器地址空间的总线事务类型。 MRL(Memory Read Line…...
【Ansible】通过role角色部署lnmp架构
目录 一.roles概述 1.roles角色 2.roles的目录层次 2.1.roles 内各目录含义解释 二.实操 1.部署nginx 2.部署MySQL 3.部署php 4.编写测试文件 三.总结 一.roles概述 1.roles角色 可以把playbook剧本里的每个play看作为一个角色,将每个角色要用到的文件、…...
springboot给属性赋值的两种方式(yaml与properties)
一,介绍 在Spring Boot中,配置文件是用来设置应用程序的各种参数和操作模式的重要部分。Spring Boot支持两种主要类型的配置文件:properties文件和YAML 文件。这两种文件都可以用来定义相同的配置,但它们在格式和表达能力上有所不…...
20240725 每日AI必读资讯
🚀最强开源模型来了!Llama3.1以405B参数领先GPT-4o - Llama3.1以405B参数领先GPT-4o和Claude3.5Sonnet,在性能上实现超越。 - Meta大幅优化训练栈,扩展模型算力规模至16000个H100GPU,提高性能。 - Llama3.1具有上下文长度扩展、…...
17_高级进程间通信 UNIX域套接字1
非命名的UNIX域套接字 第1个参数domain,表示协议族,只能为AF_LOCAL或者AF_UNIX; 第2个参数type,表示类型,只能为0。 第3个参数protocol,表示协议,可以是SOCK_STREAM或者SOCK_DGRAM。用SOCK_STR…...
大型语言模型的生物医学知识图优化提示生成
大型语言模型的生物医学知识图优化提示生成 https://arxiv.org/abs/2311.17330 https://github.com/BaranziniLab/KG_RAG 大型语言模型的生物医学知识图优化提示生成 摘要 KG-RAG框架,较好的结合了生物医学知识图谱SPOKE和LLM的优势。SPOKE是一个开放知识图谱&…...
winform datagrid 全部勾选
如果我们想要进行全选或全部取消,在数据较多的情况下,这种方法显然特别繁琐。怎么办呢? 当然是加以一个全选按钮了,选中全选按钮则全选,否则取消。笔者本想在红色圆圈位置添加全选复选框的,那样看起来更加…...
从 NextJS SSRF 漏洞看 Host 头滥用所带来的危害
前言 本篇博文主要内容是通过代码审计以及场景复现一个 NextJS 的安全漏洞(CVE-2024-34351)来讲述滥用 Host 头的危害。 严正声明:本博文所讨论的技术仅用于研究学习,旨在增强读者的信息安全意识,提高信息安全防护技能…...
LC617-合并二叉树
文章目录 1 题目描述2 思路优化代码完整输入输出 参考 1 题目描述 https://leetcode.cn/problems/merge-two-binary-trees/description/ 给你两棵二叉树: root1 和 root2 。 将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...
VisualXML全新升级 | 新增数据库编辑功能
VisualXML是一个功能强大的网络总线设计工具,专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑(如DBC、LDF、ARXML、HEX等),并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...
yaml读取写入常见错误 (‘cannot represent an object‘, 117)
错误一:yaml.representer.RepresenterError: (‘cannot represent an object’, 117) 出现这个问题一直没找到原因,后面把yaml.safe_dump直接替换成yaml.dump,确实能保存,但出现乱码: 放弃yaml.dump,又切…...
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅!
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅! 🌱 前言:一棵树的浪漫,从数组开始说起 程序员的世界里,数组是最常见的基本结构之一,几乎每种语言、每种算法都少不了它。可你有没有想过,一组看似“线性排列”的有序数组,竟然可以**“长”成一棵平衡的二…...
接口 RESTful 中的超媒体:REST 架构的灵魂驱动
在 RESTful 架构中,** 超媒体(Hypermedia)** 是一个核心概念,它体现了 REST 的 “表述性状态转移(Representational State Transfer)” 的本质,也是区分 “真 RESTful API” 与 “伪 RESTful AP…...
创客匠人:如何通过创始人IP打造实现知识变现与IP变现的长效增长?
在流量红利逐渐消退的当下,创始人IP的价值愈发凸显。它不仅能够帮助中小企业及个人创业者突破竞争壁垒,还能成为企业品牌影响力的核心资产。然而,市场上IP孵化机构鱼龙混杂,如何选择一家真正具备长期价值的合作伙伴?创…...
