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.hqwc.cn/news/80657.html

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

相关文章

C 实现Window/DOS 键盘监听事件

今天是重新复习C语言实现的第一天&#xff0c;今天想编写C 对Windwos/Dos 键盘事件的学习。但是我在安装Visual Studio 2022 没有安装MFC 框架&#xff0c;今天记录下VS追加 MFC框架。 Visual Studio 2022 追加MFC 1、打开vs&#xff0c;点击创建新项目&#xff0c;右侧滑动框…

大数据风控介绍

众所周知&#xff0c;金融是数据化程度最高的行业之一&#xff0c;也是人工智能和大数据技术重要的应用领域。随着大数据收集、存储、分析和模型技术日益成熟&#xff0c;大数据技术逐渐应用到金融风控的各个环节。个推作为专业的数据智能服务商&#xff0c;拥有海量数据资源&a…

Unity - 制作package 插件包

1.将制作的插件包代码放置一个根目录下 2.在跟目录下创建package.json文件 //package.json {"name": "com.unity.customlibrary", //插件包名:com.组织名.包名"displayName": "CustomLibrary", //显示的插件名"v…

华为OD机试 - 过滤组合字符串 - 深度优先搜索dfs算法(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…

【Linux操作系统】Linux中的信号回收:管理子进程的关键步骤

在Linux中&#xff0c;我们可以通过捕获SIGCHLD信号来实现对子进程的回收。当一个子进程终止时&#xff0c;内核会向其父进程发送SIGCHLD信号。父进程可以通过注册信号处理函数&#xff0c;并在处理函数中调用wait()或waitpid()函数来回收已终止的子进程。 文章目录 借助信号捕…

Python+TinyPNG熊猫网站自动化的压缩图片

前言 本篇在讲什么 PythonTinyPNG自动化处理图片 本篇需要什么 对Python语法有简单认知 依赖Python2.7环境 依赖TinyPNG工具 本篇的特色 具有全流程的图文教学 重实践&#xff0c;轻理论&#xff0c;快速上手 提供全流程的源码内容 ★提高阅读体验★ &#x1f449;…

WSL(centos7.0.1907.3)安装lxc

安装 1.centos的epel源提供了lxc的安装包&#xff0c;在使用epel源时首先安装epel-release包&#xff1a; yum -y install epel-release2.安装lxc软件包和依赖包 yum -y install lxc lxc-templates bridge-utils lxc-libs libcgroup libvirt 安装完成后&#xff0c;通过 lx…

SpringBoot - 两种方式刷新配置信息

一、第一种方式 ​ConfigurationProperties​不能自动刷新&#xff0c;需要手动调用contextRefresher.refresh()方法来刷新配置。 import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component;Component…

【腾讯云 TDSQL-C Serverless 产品测评】“橡皮筋“一样的数据库『MySQL高压篇』

【腾讯云 TDSQL-C Serverless 产品测评】"橡皮筋"一样的数据库 活动介绍服务一览何为TDSQL &#xff1f;Serverless 似曾相识&#xff1f; 降本增效&#xff0c;不再口号&#xff1f;动手环节 --- "压力"山大实验前瞻稍作简介资源扩缩范围&#xff08;CCU&…

无涯教程-Python - Numbers(数字)

数字数据类型存储数值&#xff0c;它们是不可变的数据类型&#xff0c;这意味着更改数字数据类型的值将导致新分配的对象。 数字对象是在您为其分配值时创建的。例如- var11 var210 您也可以使用 del 语句删除对数字对象的引用。 del语句的语法是- del var1[,var2[,var3[..…

上海交大ACM班总教头团队重磅新作,带你动手学机器学习(文末赠书4本)

目录 0 写在前面1 什么是机器学习&#xff1f;2 ACM 班总教头&#xff1a;俞勇3 动手学习机器学习赠书活动 0 写在前面 机器学习强基计划聚焦深度和广度&#xff0c;加深对机器学习模型的理解与应用。“深”在详细推导算法模型背后的数学原理&#xff1b;“广”在分析多个机器…

Vue+Axios搭建二次元动态登录页面(mp4视频格式)

最近想做一个前端登录页面&#xff0c;背景好看的&#xff0c;格式中规中矩的&#xff0c;这么难&#xff1f;我自己创一个吧&#xff01; 效果图如下&#xff1a; 源码可以参考我的github&#xff0c;复制源码即可用&#xff1a;gym02/loginPage_Vue: 使用VueAxios搭建的动态…