Spring 为什么使用三级缓存解决循环依赖

文章目录

    • 前言
      • 1. 什么是循环依赖
        • 1.1 互相依赖
        • 1.2 递归依赖
      • 2. Sping中循环依赖有什么问题?
      • 3. 什么是三级缓存
      • 4. Spring 可以解决哪些情况的循环依赖?
    • 二级缓存作用——普通循环依赖
      • 实操环节
        • 1. 实例化类A对象
        • 2. 实例化类B对象
        • 3. B对象完成创建
        • 4.继续创建A对象
    • 三级缓存作用——aop循环依赖
      • 1. AOP代理问题
      • 2. 何时生成代理对象
      • 实操环节
        • 1.实例化类A对象
        • 2. 实例化类B对象
        • 3.继续创建A对象
    • 结尾




前言

1. 什么是循环依赖

  类A需要类B,我们就叫做类A依赖类B。简单说就是⾃⼰依赖⾃⼰,或者和别的类相互依赖


1.1 互相依赖

1.2 递归依赖

在这里插入图片描述


2. Sping中循环依赖有什么问题?

  在Spring中,循环依赖指的是两个或多个Bean之间相互依赖形成的循环引用关系。具体来说,当Bean A依赖于Bean B,而Bean B又依赖于Bean A时,就形成了循环依赖。

  只有单例的 Bean 才存在循环依赖的情况,原型(Prototype)情况下,Spring 会直接抛出异常。

  循环依赖可能导致以下问题:

  • 无法完成Bean的初始化:当存在循环依赖时,Spring容器无法确定先初始化哪个Bean,因为它们相互依赖,而且都需要对方完成初始化才能继续。这可能导致Bean的初始化过程无法完成,从而引发异常。

  • 无限递归调用:当存在循环依赖时,Spring容器可能会陷入无限递归的调用中,导致系统堆栈溢出。这是因为每次获取Bean时,Spring容器需要检测循环依赖并创建实例,但由于循环依赖的存在,无法正常创建实例,从而导致无限递归调用。

在这里插入图片描述

  为了解决循环依赖问题,Spring使用了三级缓存和"提前暴露"的策略


3. 什么是三级缓存

  对于创建单例Bean,Spring创建了三个容器来存储不同时期的对象:

  1. ⼀级缓存 : Map<String,Object> singletonObjects,单例池,⽤于保存实例化、属性赋值
    (注⼊)、初始化完成的 bean 实例
  2. ⼆级缓存 : Map<String,Object> earlySingletonObjects,早期曝光对象,⽤于保存实例化完
    成的 bean 实例
  3. 三级缓存 : Map<String,ObjectFactory<?>> singletonFactories,早期曝光对象⼯⼚,⽤于
    保存 bean 创建⼯⼚,以便于后⾯扩展有机会创建代理对象
    在这里插入图片描述

4. Spring 可以解决哪些情况的循环依赖?

  Spring 不⽀持基于构造器注⼊的循环依赖,假如 AB 循环依赖,其中一方使用构造器注入,也是不支持的。

在这里插入图片描述

  为什么呢?下面二级缓存会说明白。




二级缓存作用——普通循环依赖

  只用一级缓存和二级缓存就能解决普通bean的循环依赖。
  先回顾Bean对象创建的步骤:
在这里插入图片描述

  二级缓存:又称 半成品池 存放的是实例化,但未属性赋值和初始化的Bean对象。
  一级缓存:又称 单例池 存放的是完成属性赋值和初始化的成品Bean,可以直接使用了。

  那么二级缓存是如何解决普通Bean的循环依赖的?


实操环节

  类A依赖类B,类B依赖类A。

在这里插入图片描述

1. 实例化类A对象

  对象a被实例化出来,会被放到半成品池中,当进行下一步属性赋值时,发现依赖了类B,所以开始创建对象b。
在这里插入图片描述

  如果对象b是在a的构造函数中注入的,那就完了,a无法实例化,得先去实例化b,若是b也是构造函数中注入的a,那就无解了。

public class A {private B b;@Injectpublic A(B b) {this.b = b;}
}

2. 实例化类B对象

  对象b被实例化出来,也被放到半成品池中。下一步是属性赋值,发现依赖了类A,会依次从⼀级到三级缓存查询类A对象,最终会在半成品池中找到对象a,成功将它赋值到自己的属性中。
在这里插入图片描述

3. B对象完成创建

  对象b在经过填充属性、初始化后会从半成品池里挪到单例池中,可以直接使用了。
在这里插入图片描述

4.继续创建A对象

  这时候回过头来继续对象a的属性注入,把对象b赋值给自己的属性后再经过初始化,对象a也从半成品池挪到单例池,对象a创建完成,对象b也跟着创建完成。
在这里插入图片描述



三级缓存作用——aop循环依赖

  二级缓存仍然存在问题,它无法解决AOP代理问题。

1. AOP代理问题

  AOP(面向切面)简单的说,在不改源码的情况下在原始方法前后加一些代码。
  它的底层是靠动态代理实现的,即生成一个代理类,重新原始方法,真正使用的时候其实用的是代理类对象,而非原始类对象。
  既然用的是代理类对象,单例池中应该存放的就该是代理类对象。
  二级缓存无法解决生成代理对象的问题,因为创建对象的过程很复杂,每个代理类都需要一个工厂来专门生成代理类对象。
在这里插入图片描述

  三级缓存又叫工厂池,就是用来存放生成代理类对象工厂的
在这里插入图片描述

2. 何时生成代理对象

  AOP是靠AOP处理器实现的,处理器有两个生成代理对象的方式。

  • 前置处理:在Bean对象初始化后
  • 后置处理:再Bean对象实例化前
    在这里插入图片描述
      Spring为了解决使用AOP的对象循环依赖的问题,使用了这两种处理方式。

实操环节

  类A依赖类B,类B依赖类A,类A使用了AOP。
在这里插入图片描述

1.实例化类A对象

  首先把创建类A代理对象的工厂对象放到工厂池中。
在这里插入图片描述
  类A实例化对象时发现依赖了类B,使用前置处理器生成A的代理对象,放在半成品池子中。
在这里插入图片描述

2. 实例化类B对象

  对象b找到对象a,成功属性赋值,再经过初始化成功创建,挪到单例池中待用。
  如果类B也使用了AOP,那么对象b在初始化后,会通过后置处理器生成动态代理对象,放到单例池中。
在这里插入图片描述

3.继续创建A对象

  对象b创建完,接着回头创建对象a,这个过程就很顺利了。
  当对象a完成初始化以后,因为已经是代理对象,就不会在走后置处理。
  对象a、b都创建完,会清空在二级、三级池中的相关数据,最终只在单例池中保留一份对象。
在这里插入图片描述




结尾

  尽管Spring提供了解决循环依赖的机制,但循环依赖本身是一个设计上的问题,可能导致代码的可读性和可维护性下降。因此,在编写代码时,应尽量避免出现循环依赖的情况。

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

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

相关文章

合宙Air724UG LuatOS-Air LVGL API--对象

对象 概念 在 LVGL 中&#xff0c;用户界面的基本构建块是对象。例如&#xff0c;按钮&#xff0c;标签&#xff0c;图像&#xff0c;列表&#xff0c;图表或文本区域。 属性 基本属性 所有对象类型都共享一些基本属性&#xff1a; Position (位置) Size (尺寸) Parent (父母…

2.文章复现《热电联产系统在区域综合能源系统中的定容选址研究》(附matlab程序)

0.代码链接 1.简述 光热发电是大规模利用太阳能的新兴方式&#xff0c;其储热系 统能够调节光热电站的出力特性&#xff0c;进而缓解光热电站并网带来的火电机组调峰问题。合理配置光热电站储热容量&#xff0c;能够 有效降低火电机组调峰成本。该文提出一种光热电站储热容 量配…

mysql-sql性能分析工具

一、sql执行频率 MySQL 客户端连接成功后&#xff0c;通过 show [session|global] status 命令可以提供服务器状态信息。通过如下指令&#xff0c;可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次&#xff1a; -- session 是查看当前会话 ; -- global 是查询全…

异地机房容灾备份方案,异地容灾备份方式有哪些

任何时候&#xff0c;我们都不能避免自然灾害、硬件问题、黑客攻击等事故。这些事情可能会导致数据中心的偏瘫&#xff0c;甚至影响整个业务的正常使用。因此&#xff0c; 机房容灾备份计划已成为确保业务连续性的关键对策。 一、异地机房容灾备份方案是什么&#xff1f; 国外…

运维高级学习--Docker(二)

1、使用mysql:5.6和 owncloud 镜像&#xff0c;构建一个个人网盘。 #拉取mysql5.6和owncloud镜像 [rootlocalhost ~]# docker pull mysql:5.6 [rootlocalhost ~]# docker pull owncloud [rootlocalhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED …

接口自动化yaml文件读取与写入

前言 在走进yaml文件之前大家应该都很想知道他是用来干嘛的&#xff1f; 是的是的&#xff0c;他是用来做接口自动化测试的。 我们一起来学习他吧&#xff01;——&#xff08;一定要收藏带走哦❤&#xff09; 1、yaml文件有什么作用呢&#xff1f; ①可作为配置文件使用—…

maven工具-maven的使用-镜像仓库、本地仓、IDEA使用maven

Maven 一、为什么使用maven 添加第三方jar包jar包之间的依赖关系处理jar包之间的冲突获取第三方jar包将项目拆分成多个工程模块实现项目的分布式部署 二、maven简介 ​ Maven项目对象模型(POM)&#xff0c;可以通过一小段描述信息来管理项目的构建&#xff0c;报告和文档的…

Pytorch06-复杂模型构建

https://github.com/ExpressGit/Pytorch_Study_Demo 1、PyTorch 复杂模型构建 1、模型截图2、模型部件实现3、模型组装 2、模型定义 2.1、Sequential 1、当模型的前向计算为简单串联各个层的计算时&#xff0c; Sequential 类可以通过更加简单的方式定义模型。2、可以接收…

学习ts(七)泛型

定义 泛型允许我们在强类型程序设计语言中编写代码时使用一些以后才指定的类型&#xff0c;在实例化时作为参数指明这些类型。在ts中&#xff0c;定义函数、接口或类的时候&#xff0c;不预先定义好具体的类型&#xff0c;而在使用的时候在指定类型的一种特性。 例子&#xff…

数据库——Redis 单线程模型详解

文章目录 Redis 基于 Reactor 模式来设计开发了自己的一套高效的事件处理模型 &#xff08;Netty 的线程模型也基于 Reactor 模式&#xff0c;Reactor 模式不愧是高性能 IO 的基石&#xff09;&#xff0c;这套事件处理模型对应的是 Redis 中的文件事件处理器&#xff08;file …

day 38 | ● 518. 零钱兑换 II ● 377. 组合总和 Ⅳ

518. 零钱兑换 II 这道题就是完全背包问题&#xff0c;因为可以选择的数量是无限的。所以第二层的遍历顺序就是从前往后。 因为是次数问题&#xff0c;递推公式是 的&#xff0c;初值应该设定为dp【0】 1&#xff0c;否则无法进行累加。 func change(amount int, coins []i…

线性代数的学习和整理5: 矩阵的加减乘除及其几何意义

目录 1 矩阵加法 1.1 矩阵加法的定义 1.2 加法的属性 1.2.1 只有同类型&#xff0c;相同n*m的矩阵才可以相加 1.2.1 矩阵加法的可交换律&#xff1a; 1.2.2 矩阵加法的可结合律&#xff1a; 1.3矩阵加法的几何意义 2 矩阵的减法 2.1 矩阵减法定义和原理基本同 矩阵的…

正则表达式一小时学完

闯关式学习Regex 正则表达式&#xff0c;我感觉挺不错的&#xff0c;记录一下。 遇到不会的题&#xff0c;可以评论交流。 真的很不错 链接 Regex Learn - Step by step, from zero to advanced.

Linux常用命令——dhcrelay命令

在线Linux命令查询工具 dhcrelay 使用dhcrelay命令可以提供中继DHCP和BOOTP请求 补充说明 dhcrelay命令使用dhcrelay命令可以提供中继DHCP和BOOTP请求&#xff0c;从一个没有DHCP服务器的子网直接连接到其它子网内的一个或多个DHCP服务器。该命令在DHCP中继服务器上使用&am…

广播与组播

广播含义 数据包发送方式只有一个接收方&#xff0c;称为单播如果同时发给局域网中的所有主机&#xff0c;称为广播只有用户数据报&#xff08;使用UDP协议&#xff09;套接字才能广播广播地址 一个网络内主机号全为1的IP地址为广播地址 发到该地址的数据包被所有的主机接收 2…

消息中间件相关面试题

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱发博客的嗯哼&#xff0c;爱好Java的小菜鸟 &#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&#x1f44d;一下博主哦 &#x1f4dd;社区论坛&#xff1a;希望大家能加入社区共同进步…

关于css 的选择器和 css变量

css 选择器 常用的选择器 1. 后代选择器&#xff1a;也就是我们常见的空格选择器&#xff0c;选择的对象为该元素下的所有子元素 。例如&#xff0c;选择所有 元素下的 元素 div p{font-size:14px}2. 子元素选择器 ‘>’ 选择某元素下的直接子元素。例如&#xff0c;选择所…

高防护等级工业RFID读写器

工业环境恶劣&#xff0c;RFID工业读写器要能够在工业领域应用必须满足一定的防护等级&#xff0c;才能避免外界灰尘油污对设备产生影响&#xff0c;因此企业选择一款高防护等级的读写器尤为重要。下面本文就为大家介绍一下工业读写器对应的防护等级&#xff0c;给大家一个参考…

数据结构入门 — 顺序表详解

前言 数据结构入门 — 顺序表详解 博客主页链接&#xff1a;https://blog.csdn.net/m0_74014525 关注博主&#xff0c;后期持续更新系列文章 文章末尾有源码 *****感谢观看&#xff0c;希望对你有所帮助***** 文章目录 前言一、顺序表1. 顺序表是什么2. 优缺点 二、概念及结构…

c++ 命名空间

1. 基本概念  1.1 定义与使用  1.2 using语句2. 进阶语法  2.1 内嵌名字空间  2.2 扩展性  2.3 全局作用域3. 小结 1. 基本概念 名字空间本质上是自定义作用域&#xff0c;由于C设计的初衷是开发大规模软件&#xff0c;大量的软件库必然会加剧全局符号&#xff08;变量、…