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

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.mzph.cn/news/62887.shtml

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

相关文章

ctfshow-web14

0x00 前言 CTF 加解密合集CTF Web合集 0x01 题目 0x02 Write Up 首先看到这个,swith,那么直接输入4,则会打印$url的值 然后访问一下 查看一下,发现完整的请求是http://c7ff9ed6-dccd-4d01-907a-f1c61c016c15.challenge.ctf.sho…

如何用VMware虚拟机连上Xshell

目录 前言废话1.1设置虚拟机设置1.2 设置虚拟网络编辑器方法一:方法二: 1.3 配置静态IP地址1.4 Xshell连接虚拟机2.1 解决可能出现的一些问题2.1.1 虚拟机Ping不通网络2.1.2 我可以Ping通百度了,但是宿主机和虚拟机互相Ping不通。2.1.3 更离谱…

苍穹外卖01-项目概述、环境搭建

项目概述、环境搭建 课程内容 软件开发整体介绍苍穹外卖项目介绍开发环境搭建导入接口文档Swagger 项目整体效果展示: 管理端-外卖商家使用用户端-点餐用户使用当我们完成该项目的学习,可以培养以下能力: 1. 软件开发整体介绍 作为一名软…

Python爬虫基础之正则表达式

目录 一、什么是正则表达式? 二、re.compile()编译函数 三、group()获取匹配结果函数 四、常用匹配规则 4.1匹配单个字符 4.2匹配前字符次数 4.3匹配原生字符串 4.4匹配字符串开头和结尾 4.5分组匹配 五、re.match()开头匹配函数 六、re.search()全文搜索…

JavaScript Web APIs -03 事件流、事件委托、其他事件(加载、滚动、尺寸)

Web APIs - 03 文章目录 Web APIs - 03事件流捕获和冒泡阻止冒泡 事件委托其他事件页面加载事件元素滚动事件页面尺寸事件 元素尺寸与位置 进一步学习 事件进阶,实现更多交互的网页特效,结合事件流的特征优化事件执行的效率 掌握阻止事件冒泡的方法理解事…

【MySQL系列】索引的学习及理解

「前言」文章内容大致是MySQL索引的学习。 「归属专栏」MySQL 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、索引概念二、从硬件角度理解2.1 磁盘2.2 结论 三、从软件角度理解四、共识五、索引的理解5.1 一个现象和结论5.2 对Page进行建模5.3 索引可以采用的数据结构5.…

opencv-人脸识别

对https://blog.csdn.net/weixin_46291251/article/details/117996591这哥们代码的一些修改 import cv2 import numpy as np import os import shutil import threading import tkinter as tk from PIL import Image, ImageTkchoice 0# 首先读取config文件,第一行…

多线程(额外扩展)(面试会用)

1 线程状态 1.1 状态介绍 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。线程对象在不同的时期有不同的状态。那么Java中的线程存在哪几种状态呢?Java中的线程 状态被定义在了java.lang.Thread.State枚…

使用飞桨实现的第一个AI项目——波士顿的房价预测

part1.首先引入相应的函数库: 值得说明的地方: (1)首先,numpy是一个python库,主要用于提供线性代数中的矩阵或者多维数组的运算函数,利用import numpy as np引入numpy,并将np作为它的别名 part…

基于OFDM的水下图像传输通信系统matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 function [rx_img] func_TR(tx_img, num_path, pathdelays, pathgains, snr) rng(default); …

【C++】set和map

set和map 1. 预备知识2. set2.1 set的概念2.2 set的常见接口 3. multiset4. map4.1 map的概念4.2 map的常见接口 5. multimap6. 练习 1. 预备知识 set和map是关联式容器&#xff0c;里面存储的不是元素本身&#xff0c;存储的是<key,value>结构的键值对&#xff0c;比ve…

日本橙皮书数据库—《医疗用医药品质量情报集》

日本橙皮书是一份关于医疗用医药品质量情报的汇总报告&#xff0c;由日本厚生劳动省发布。它主要涵盖了药品的品质再评价信息&#xff0c;特别是针对特定历史阶段的产品&#xff0c;笔者总结信息如下&#xff1a; ①日本橙皮书数据库包含了一系列药品的详细信息&#xff0c;如…

43、基于 springboot 自动配置的 spring mvc 错误处理,就是演示项目报错后,跳转到自定义的错误页面

Spring MVC 的错误处理&#xff1a;基于 SpringBoot 自动配置之后的 Spring MVC 错误处理。 就是访问方法时出错&#xff0c;然后弄个自定义的错误页面进行显示。 ★ 两种错误处理方式 方式一&#xff1a; 基于Spring Boot自动配置的错误处理方式&#xff0c;只要通过属性文件…

原生js实现轮播图及无缝滚动

我这里主要说轮播图和无缝滚动的实现思路&#xff0c;就采用最简单的轮播图了&#xff0c;当然实现的思路有很多种&#xff0c;我这也只是其中一种。 简单轮播图的大概结构是这样的&#xff0c;中间是图片&#xff0c;二边是箭头可以用来切换图片&#xff0c;下面的小圆点也可以…

Unity3d C#实现调取网络时间限制程序的体验时长的功能

前言 如题的需求应该经常在开发被提到&#xff0c;例如给客户体验3–5天的程序&#xff0c;到期后使其不可使用&#xff0c;或者几年的使用期限。这个功能常常需要使用到usb加密狗来限制&#xff0c;当然这也的话就需要一定的硬件投入。很多临时提供的版本基本是要求软件来实现…

代理模式 静态代理和动态代理(jdk、cglib)——Java入职第十一天

一、代理模式 一个类代表另一个类去完成扩展功能,在主体类的基础上,新增一个代理类,扩展主体类功能,不影响主体,完成额外功能。比如买车票,可以去代理点买,不用去火车站,主要包括静态代理和动态代理两种模式。 代理类中包含了主体类 二、静态代理 无法根据业务扩展,…

在ubuntu上安装ns2和nam(ubuntu16.04)

在ubuntu上安装ns2和nam 版本选择安装ns2安装nam 版本选择 首先&#xff0c;版本的合理选择可以让我们避免很多麻烦 经过测试&#xff0c;ubuntu的版本选择为ubuntu16.04&#xff0c;ns2的版本选择为ns-2.35&#xff0c;nam包含于ns2 资源链接(百度网盘) 链接:https://pan.bai…

简单数学题:找出最大的可达成数字

来看一道简单的数学题&#xff1a;力扣2769. 找出最大的可达成数字 题目描述的花里胡哨&#xff0c;天花乱坠&#xff0c;但这道题目非常简单。我们最多执行t次操作&#xff0c;只需每次操作都让x-1&#xff0c;让num1&#xff0c;执行t次操作后&#xff0c;x就变为xt&#xff…

腾讯音乐如何基于大模型 + OLAP 构建智能数据服务平台

本文导读&#xff1a; 当前&#xff0c;大语言模型的应用正在全球范围内引发新一轮的技术革命与商业浪潮。腾讯音乐作为中国领先在线音乐娱乐平台&#xff0c;利用庞大用户群与多元场景的优势&#xff0c;持续探索大模型赛道的多元应用。本文将详细介绍腾讯音乐如何基于 Apach…

基于蛇优化算法优化的BP神经网络(预测应用) - 附代码

基于蛇优化算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于蛇优化算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.蛇优化优化BP神经网络2.1 BP神经网络参数设置2.2 蛇优化算法应用 4.测试结果&#xff1a;5.Matlab代…