Spring 如何解决循环依赖问题 - 三级缓存

news2025/1/16 14:00:09

1. 什么是循环依赖问题 ?

        循环依赖问题是指对象与对象之间存在相互依赖关系,而且形成了一个闭环,导致两个或多个对象都无法准确的完成对象的创建和初始化。

两个对象间的循环依赖:

多个对象间的循环依赖 :

解决 Spring 中的循环依赖有两个前提条件:

  1. 存在相互依赖的 Bean 必须是单例的 Bean;
  2. 依赖注入 Bean 的方式不能是构造方法注入。

为什么要满足条件 1:

        如果说相互依赖的 Bean 不是单例的,那么 A 需要 B,就得重新 new 一个  B,B 需要 A,就得重新 new 一个 A,新 new 出来的 A 又需要 B,新 new 出来的 B 又需要 A,又进入最初的步骤了,这样下去将会无穷尽也,就根本解决不了了,所以要求存在相互依赖的 Bean 必须是单例的 Bean。

为什么要满足条件 2:

在了解这个原因之前,必须得先了解 Bean  的生命周期,不太清楚的可以先去看看这篇博客:https://blog.csdn.net/xaiobit_hl/article/details/128025295

        因为构造方法的执行时机太靠前了,如果依赖注入 Bean 的方式是构造方法注入,就会导致 A 需要 B 的时候,B 压根还没执行到属性赋值那一步,而 B 需要 A 的时候,A 也压根没执行到属性赋值这一步,这就是先有鸡还是先有蛋的问题了。(A 依赖 B 对象的时候,需要 DI 注入 B 对象到当前对象中,这一步在 Bean 的生命周期中也叫做属性赋值,而需要给 B 进行赋值,B 对象就得先执行完实例化、属性赋值、初始化这三个生命周期)

2. Spring 三级缓存如何解决循环依赖问题 ?

就拿两个对象间的循环依赖来举例:

首先单例对象一定需要存储下来,所以需要一个一级缓存来存储完全初始化好的对象:

A 和  B 相互依赖时, Bean 的执行流程:

A 和 B 相互依赖的执行流程(不牵扯 AOP):

  1. 实例化 A 对象;
  2. 对 A 的依赖对象 B 进行属性赋值,发现 B 对象还没有初始化好,就需要先初始化 B 对象;
  3. B  对象实例化;
  4. 对 B 的依赖对象 A 进行属性赋值,此时 A 还是一个半成品,还没有初始化好,于是 A 对象的引用地址赋值给 B 中的依赖对象;
  5. B 对象进行初始化,B 对象初始化完之后,A 对象也就属性赋值完毕;
  6. A 对象执行初始化了。

        上述流程中,第 4  步,B 对象在给 A 对象进行属性赋值的时候,此时的 A 对象还是个半成品,那么 B 在给 A 属性进行属性赋值的时候,这个半成品也是需要进行存起来的,否则 B 对象后续的流程就无法执行下去了,这时候就有了二级缓存,二级缓存就是用来存放没有初始化好的对象。

        本来有了一级缓存,二级缓存就可以解决循环依赖问题了,但是第三者 AOP 的出现,就导致故事变得复杂起来了。

【前置铺垫】程序中如果使用了 AOP(动态代理),那么 Spring 中存储的就是代理对象,而不是目标对象了,那么在构建代理对象的时候,一定是需要目标对象的,所以代理对象既不能存储在一级缓存,也不能存储在二级缓存,但是这个代理对象是一定要存储下来的,否则就变成多例的了,这就违背了解决循环依赖的前置条件 1,所以这时候就引入了三级缓存。(代理对象中存放的是工厂对象 FactoryBean,代理对象就是通过工厂对象生成的)

这三个缓存在源码中其实就是三个 map :

  1. singletonObjects:一级缓存(ConcurrentHashMap)
  2. singletonFactories:三级缓存(HashMap)
  3. earlySingletonObjects:二级缓存(ConcurrentHashMap)

为什么三级缓存使用 HashMap:(doCreateBean())

因为它里面放的是一个 lambda 表达式,它不是一个真正的对象,所以它就不怕线程安全问题。

所以 Spring 里面解决循环依赖的问题,就是引入了三级缓存来解决的。

程序中如果使用到了 AOP,那么前面 A 依赖 B,B 依赖 A 的执行流程就需要稍作改变:

         

        上述流程执行完毕后,一级缓存中就有了 A 对象和 B 对象了,其他对象需要依赖这俩对象时,就可以从一级缓存中去取了。 

上述流程 4 中,B 进行属性赋值时,寻找 A 对象的流程源码如下:

B 对象执行属性赋值时,寻找 A 对象流程:

        先从一级缓存中找,没找到,就去二级缓存找,也没找到,就去三级缓存找,此时就会执行 A 的 lambda 表达式,此时 A 对象不管是代理对象还是目标对象,都会被晋级到二级缓存中(考虑性能),当其他对象中依赖 A 对象时,就不再需要从三级缓存中拿了,而是直接从二级缓存中取;

        虽然循环依赖问题确实存在,也不可避免,但是 SpringBoot 3.0 以及 Spring framework 6.0 之后,默认情况下就关闭了对于循环依赖的一个支持了,也就是说在 Spring 高版本底下,如果存在循环依赖的问题,Spring 就会告诉你,你的项目中有循环依赖,项目启动失败。


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

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

相关文章

MobaXtermV10.7安装步骤

目录 1,打开​编辑 2,填写自己的虚拟机IP和用户名,点机OK 3,设置 MobaXterm是一款增强型远程连接工具,主要用于Windows的增强终端,带有X11服务器、选项卡式SSH客户端、网络工具等。在一个Windows应用程序中&#xff…

acunetix2023安装教程

1、解压之后一键安装exe文件 2、将解压出来的Awv2023.6[Windows]文件夹下的wvsc.exe文件放置于AWVS安装目录,与原文件进行替换,如图所示。(注:如果是默认安装,则文件位置位于C:\Program Files (x86)\Acunetix\14.2.210…

C语言“牵手”京东商品详情数据方法,京东商品详情API接口,京东API申请指南

京东是中国最大的自营式电商企业,在线销售计算机、手机及其它数码产品、家电、汽车配件、服装与鞋类、奢侈品、家居与家庭用品、化妆品与其它个人护理用品、食品与营养品、书籍与其它媒体产品、母婴用品与玩具、体育与健身器材以及虚拟商品等。 京东平台的商品详情…

12.6V三节锂电池升压充电管理IC

HU5810C是5V输入升压充电12.6V1.2A给三节锂电池充电芯片 输入小电流不会拉死,温度60建议1000-1100MA带NTC热敏温度电流保护。HU5810C是一款5V输入,1.2A充电电流,支持三节锂电池串联应用 ,锂离子电池的升压充电管理IC。HU4059C集成功率MOS,采用异步开关架…

pycharm Failed to connect to github.com port 443: Timed out

使用浏览器发现必需打开代理才能访问github, 故有可能是pycharm没有设置代理. 在设置中选择自动代理模式.

【LeetCode】205. 同构字符串 - 数组

这里写自定义目录标题 2023-8-29 16:32:00 205. 同构字符串 2023-8-29 16:32:00 详细通俗的思路分析,多解法 重新了一遍解法二,下次再写这个题目,我要试一试用HashMap class Solution {public boolean isIsomorphic(String s, String t)…

视频监控/视频汇聚/视频云存储EasyCVR平台HLS流集成在小程序无法播放问题排查

安防视频/视频云存储/视频集中存储EasyCVR视频监控综合管理平台可以根据不同的场景需求,让平台在内网、专网、VPN、广域网、互联网等各种环境下进行音视频的采集、接入与多端分发。在视频能力上,视频云存储平台EasyCVR可实现视频实时直播、云端录像、视频…

Mac系统Anaconda环境配置Python的json库

本文介绍在Mac电脑的Anaconda环境中,配置Python语言中,用以编码、解码、处理JSON数据的json库的方法;在Windows电脑中配置json库的方法也是类似的,大家可以一并参考。 JSON(JavaScript Object Notation)是一…

四川玖璨电子商务有限公司:抖店运营攻略

抖店运营,是指在抖音平台上进行电商销售的一种新型商业模式。随着抖音平台越来越受到年轻人的喜爱和关注,抖店运营正变得越来越重要。那么,抖店运营应该如何做呢?我们来谈谈抖店运营的一些攻略。 第一,选对产品。选择…

论文阅读_扩散模型_SDXL

英文名称: SDXL: Improving Latent Diffusion Models for High-Resolution Image Synthesis 中文名称: SDXL:改进潜在扩散模型的高分辨率图像合成 论文地址: http://arxiv.org/abs/2307.01952 代码: https://github.com/Stability-AI/generative-models 时间: 2023-…

无门槛访问ChatGPT升级版-数据指北AI

大家好,我是脚丫先生 (o^^o) 给小伙伴们介绍ChatGPT升级版不需要任何门槛,不需要单独搞账号,只要邮箱登录的方式,即可访问平台,以用户体验为首要,让所有人都能无门槛的使用目前市面上最强大的AI智能聊天&a…

vue3+antdesign table实现表格行颜色

实现效果&#xff1a; 代码&#xff1a; html: <a-table:columns"stockColumns":data-source"stockData"class"ant-table-striped":rowClassName"rowClassName"></table> js: const rowClassName computed(() > {re…

打工人日常带饭的「不敷衍攻略」,贼实用

点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 卷圈 运营 / SandLiu 卷圈 监制 / 姝琦 文案 / 粒粒 产品统筹 / bobo 场地支持 / 声湃轩北京录音间 不知你怎么想&#xff0c;反正就我们的观察来看&#xff0c;带饭上班&#xff0c;绝对处于办公室午餐鄙视链的顶端。…

通达信50日均线上的股票数占比指标公式,衡量大盘强弱

在《以交易为生》书中&#xff0c;作者埃尔德介绍了50日均线上的股票数占比指标&#xff0c;用来衡量大盘的强弱以及捕捉市场转机。50日均线上的股票数占比指标公式计算方法很简单&#xff0c;用高于其50日均线的股票数量除以股票总数。假设沪深A股总数有5000只&#xff0c;有2…

Promise学习笔记

Promise 第1章&#xff1a;Promise的理解和使用1.1. Promise是什么 ?1.1.1. 理解1.1.2. promise的状态改变1.1.3. promise的基本流程1.1.4. promise的基本使用 1.2. 为什么要用Promise?1.2.1. 指定回调函数的方式更加灵活1.2.2. 支持链式调用, 可以解决回调地狱问题 1.3. 如何…

数据库中的条件索引使用

数据库条件索引 在逻辑删除相关的表中&#xff0c;设置普通唯一索引在多个逻辑上已删除的元组中可能发生唯一性冲突&#xff0c;即不允许存在两个相同的已删除元组&#xff0c;同时在存在已删除元组时也不允许插入相同值的新元组。此时可以通过设置条件索引&#xff0c;使唯一…

Ubuntu 启动出现grub rescue

​ 一&#xff0c;原因 原因&#xff1a;出现 “grub rescue” 错误通常表示您的计算机无法正常引导到操作系统&#xff0c;而是进入了 GRUB&#xff08;Grand Unified Bootloader&#xff09;紧急模式。这可能是由于引导加载程序配置错误、硬盘驱动器损坏或其他引导问题引起…

功率放大器有什么作用和功能呢

功率放大器是一种被广泛应用于各种电子设备和系统中的电路或器件。它的主要功能是将输入信号的功率增加到更高的水平&#xff0c;以满足各种应用需求。功率放大器在通信、音频、视频、无线电和电力行业等领域都扮演着重要的角色。 信号放大作用 功率放大器最基本的作用是对输入…

Spring Cloud 面试题——Spring Cloud Gateway

目录 1.什么是 API 网关&#xff1f;有什么作用&#xff1f;2.有哪些常见的网关&#xff1f;3.什么是 Spring Cloud Gateway&#xff1f;有什么作用&#xff1f;有什么优缺点&#xff1f;4.✨Spring Cloud Gateway 的工作流程是什么样的&#xff1f;5.✨Spring Cloud Gateway 中…

百度中文点选验证码研究

之前百度都是只有旋转验证码&#xff0c;最近总是出现中文点选验证码。而且中文数量比较多&#xff0c;大图中有固定的7个中文字符需要识别。 我首先想到使用通用的中文识别&#xff0c;当时我尝试了很多出名的中文识别但是效果都非常差&#xff0c;基本上全错&#xff0c;完全…