【Spring源码】21. 关于循环依赖的N个问题

news2025/1/17 3:51:26

完成了applyMergedBeanDefinitionPostProcessors()方法,后面有一段关于判断Bean是否需要提前曝光的逻辑(如下图红框框中部分)

在这段逻辑中涉及到了著名的循环依赖,提到循环依赖基本必讲三级缓存,好吧,这篇就是介绍他们这对“依存”CP的(。・ω・。)ノ

前置知识

什么是循环依赖

不同的Bean之间的依赖关系形成了一个闭环(比如ABean依赖了BBean,同时BBean又依赖了ABean),我们把这种依赖关系就称为循环依赖

如何解决这种“死循环”?

缓存“半成品”(只完成实例化未进行初始化的对象)

为什么使用多级缓存?

由于获取Bean对象都是通过BeanName获取的,所以同一个Bean的半成品、成品,以及创建Bean代理对象的lambda表达式不能放在同一个缓存(Map)中

  • 一级缓存存放成品(包括普通对象/代理对象的完整对象)

  • 二级缓存存放半成品(完成实例化,但未完成初始化,提前暴露的对象)

  • 三级缓存存放创建bean的代理对象的lambda表达式(回调机制)

N个相关问题

我们在获取完整对象的时候,分为了两个步骤,实例化和初始化,这也是三级缓存能解决循环依赖的根本原因

  1. 初始化环节的填充属性阶段有没有可能会改变当前对象的地址空间?

不会,但是,有另一种情况,即Spring在使用的时候会配置AOP,需要生成具体的代理对象,那么问题又来了

  1. 生成代理对象之前,要不要生成普通对象?

要的

  1. 我们能否确定什么时候会调用具体的对象(无论是普通对象还是代理对象)?不能,在调用对象的时候,如果检测到当前对象需要被代理,那么直接直接创建代理对象覆盖即可

  1. 那如何能在需要的时候能立马创建代理对象?

类似于观察者(但实际不是),传递一个创建代理对象的匿名内部类,当需要调用的时候,直接调用该匿名内部类(lambda)来生成代理对象(类似于回调机制)

  1. 有没有可能,在Spring中同时存在同一个beanNamel的普通对象和代理对象?有

  1. 如果有的话,那么我在调用的时候是根据beanName来获取对象,此时我要获取到两个对象吗?那么我应该用哪个?

创建两个缓存,分别存储

  1. 如果两个对象分别存储,那么优先获取哪个?

代理对象中应该包含了普通对象的恶所有功能,如果一个对象被代理了,那么此普通对象就可以不存在,只要代理对象

插个知识点,初始化包含哪些环节?

  1. 执行aware接口所对应的方法

  1. 执行beanPostProcessor中的before方法

  1. 执行init-method

  1. 执行beanPostProcessor中的after方法

  1. 上面步骤执行完成后是为了获取到一个完整的成品对象,但是在初始化前我们能确定哪一个对象需要生成代理对象吗?

不能

  1. 半成品是干嘛的?

为了在进行属性注入时,可以直接获取半成品的引用地址,保证对象能够被找到,注入能成功,而半成品对象在堆空间中是否设置有属性值并不重要

  1. 那么能否把所有的bean所需的创建代理对象的lambda表达式都放到三级缓存中?

当然可以,由于三级缓存只是一个回调机制,可以将所有bean对象需要的创建代理对象的lambda表达式放到三级缓存中,后续如果需要调用,直接从三级缓存中调用执行即可,如果不需要,在生成完整对象之后可以清除三级缓存中的lambda表达式

  1. 什么时候要生成具体的代理对象呢?

  1. 在进行属性注入的时候,调用该对象生成的时候,检测是否需要被代理,如果需要,直接创建代理对象

  1. 在整个过程中,没有其他对象有当前对象的依赖,那么在生成最终的完整对象之前(BeanPostProcessor的after方法)生成代理对象即可

总结

三级缓存在进行对象查找的时候,顺序是?

一级缓存 -> 二 级缓存 -> 三级缓存

为什么要存在三级缓存?

为了生成代理对象(否则二级缓存即可)

解决循环依赖的根本原因?

实例化和初始化是分开处理的

源码分析

前面介绍了循环依赖、三级缓存的相关前置内容,我们来分析下这段源码(。・ω・。)ノ

判断是否符合提前曝光的条件

先是定义了一个earlySingletonExposure布尔对象,用于判断当前bean是否需要提前曝光,需要同时满足三个条件(即下面三个条件同时为true):

  • mbd.isSingleton() 是否为单例

  • this.allowCircularReferences 是否允许循环依赖

  • isSingletonCurrentlyInCreation(beanName) 当前bean是否正在创建中

条件1 : mbd.isSingleton()

具体在哪里设置的没有找到= =,,这个个坑,后续来填

条件2 : this.allowCircularReference

其中this.allowCircularReferences的默认值是true

但其实这个值是可以设置的,各位客官记不记得this.allowCircularReferences是在哪里设置的?

我们在一开始创建容器对象的时候执行过一个o btainFreshBeanFactory()方法,在这个方法中的refreshBeanFactory()方法中的customizeBeanFactory()里对BeanFactory进行了定制化,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及是否允许循环依赖

条件3 : isSingletonCurrentlyInCreation(beanName)

这也是个坑,在哪里放进集合中的

singletonsCurrentlyInCreation这个集合的创建方式很特别

Collections.newSetFromMap(new ConcurrentHashMap<>(16))这是一个构造线程安全Set的方式,这种方式支持并发的读和更新,相较采用Collections.syncrhonizedSet(HashSet)的方式,不会在迭代中进行读取的时候发生CME(ConcurrentModicationException)的异常。

符合后处理

为避免后期出现循环依赖,可以在bean初始化完成前将创建实例的ObjectFactory加入工厂(三级缓存)中

判断一级缓存中是否包含当前beanName

  • 包含则不做任何处理;

  • 不包含,执行以下操作:

  • 将beanName放入单例工厂中

  • 从早期单例集合(earlySingletonObjects)中删除(从二级缓存中移除)

  • 添加到已注册的集合(registeredSingletons)中

总结

就是往三级缓存中放入创建当前对象lambda表达式,方便后续进行回调调用

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

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

相关文章

CANOpen中SDO和PDO的COB-ID理解

CAN 总线是一种串行通信协议&#xff0c;具有较高的通信速率的和较强的抗干扰能力&#xff0c;可以作为现场总线应用于电磁噪声较大的场合。由于 CAN 总线本身只定义ISO/OSI 模型中的第一层&#xff08;物理层&#xff09;和第二层&#xff08;数据链路层&#xff09;&#xf…

(8)go-micro微服务Mysql配置

文章目录一 gorm介绍二 gorm安装1.1 下载依赖1.2 使用MySQL驱动三 CURD操作1. 查询1.1 单行查询1.2 多行查询2. 插入数据3. 更新数据4. 删除数据四 初始化连接五 使用六 最后一 gorm介绍 Go语言中的database/sql包提供了保证SQL或类SQL数据库的泛用接口&#xff0c;并不提供具…

redis: jedis连接超时(需要手动注入连接超时检测的配置)

相关版本说明 服务端&#xff1a; redis_version: 6.2.8 客户端&#xff1a; springBoot: 2.7.7 jedis: 3.8.0 问题 偶发redis连接超时&#xff0c;刷新就又好了&#xff0c;服务日志错误信息如下&#xff1a; JedisConnectionException: Unexpected end of stream.原因 …

Linux利用httpd搭建局域网yum源

本例环境&#xff1a;vmwareworkstation16 proCentOS7.9 mast节点&#xff1a;192.168.195.110 用于配置httpd并发布本地yum源 node节点&#xff1a;192.168.195.111 用于验证mast节点的yum源是否可用 思路&#xff1a;1.在mast节点挂载/上传镜像后配置本地yum源 2.利用本…

JSP三种脚本

脚本可以编写Java语句、变量、方法或表达式。 1.普通脚本 语法: <% Java代码%> <% page contentType"text/html;charsetUTF-8" language"java" %><html><head> <title>Title</title></head><body>&l…

对u盘的分区进行删除和格式化

一、说明 当usb盘&#xff0c;或者SD卡用作启动盘后&#xff0c;将出现多个盘符、多个分区&#xff1b;若将此盘重新当文件盘&#xff0c;需要删除以前的分区&#xff0c;并重新格式化后&#xff0c;才能使用。 二、使用Diskpart在Windows 10中对USB进行分区删除 2.1 尝试磁盘…

重启之后,台式机网络不能连接怎么办

目录 1.问题 2.排查过程 3.心得 1.问题 前天电脑意外断电后,再启动发现网络变成了未连接状态.查看本地连接显示已启动,但IPv4和IPv6未连接.当时做了一些尝试,没有收到效果,直到今天问题才得以解决. 2.排查过程 Windows网络诊断为:DNS服务器未响应.后来花了一部分时间在DNS…

ruoyi-vue集成magic-api(一)

ruoyi虽然带了强大的代码生成器&#xff0c;面对比较通用的CRUD还是游刃有余的&#xff0c;但在项目开发阶段&#xff0c;需求总是经常变化的&#xff0c;数据结构和逻辑也经常变化&#xff0c;我们需要的是快速验证功能逻辑&#xff0c;代码生成器可帮不上忙&#xff0c;每次需…

一、java编写登录功能

java编写登录功能 文章目录java编写登录功能前言编程学习记录一、登录逻辑简述二、代码实现1.创建USER表2.前端代码3.创建User类4.创建LoginServlet类5.创建JDBCUtils类6.创建UserDao类7.创建FailServlet类9.创建SuccessServlet 类11.配置tomcat 服务12.启动服务前言 编程学习…

SpringCloud Netfllix复习之Hystrix

文章目录写作背景Hystrix是什么Hystrix的核心功能上手实战RestTemplate整合HystrixOpenFeign整合HystrixOpenFeign与Hystrix整合的各种参数如何配置&#xff1f;源码验证基于HystrixCommand注解实现熔断源码分析初始化资源线程池的源码OpenFeign与Hystrix整合执行请求的源码写作…

Java多线程:创建多线程的“四种“ 方式

Java多线程&#xff1a;创建多线程的"四种" 方式 每博一文案 白马笑西风写道&#xff1a;江南有杨柳&#xff0c;有燕子&#xff0c;金鱼......汉人中有的是英俊勇武的少年&#xff0c;倜傥潇洒的少年...... 但这个美丽的姑娘就像故高昌国人那样固执&#xff1a;&qu…

buctoj-2023寒假集训-进阶训练赛(八)

问题 A: 分离出整数n从右边数第k个数字&#xff0c;递归实现 题目描述 在程序中定义一函数digit(n,k)&#xff0c;它能分离出整数n从右边数第k个数字。 输入 正整数n和k。 输出 第k个数字(若不存在则输出0&#xff09; 样例输入 31859 3 样例输出 8 #include<bits/stdc.h&g…

电商直播小程序核心功能有哪些?电商直播小程序代码分析

一个优质的电商直播小程序&#xff0c;必须带有后台管理&#xff0c;模块功能分工明确&#xff0c;可以让商家及时管理商品。在管理后台端又分为会员、商品、订单、店铺、直播、分销、优惠券、物流、数据等功能列表栏&#xff0c;基本功能较完善。下文小编将为大家讲解一下电商…

Linux命令行中 git 的使用

文章目录&#xff1a;什么是gitgitee新建仓库git提交代码1.同步远程仓库代码 - git pull2.查看本地仓库的状态 - git status3.添加代码到本地.git缓冲区 - git add4.推送代码到本地仓库.git中 - git commit5.同步本地仓库.git的内容到远程仓库 - git push什么是git Git 是一个…

2023年了,浏览器竟然还有新玩法,能看热搜能领券

在移动互联网时代&#xff0c;手机浏览器是手机中不可缺少的APP之一。我们经常使用手机浏览器查资料&#xff0c;看新闻&#xff0c;看小说等等。如今&#xff0c;手机浏览器的功能越来越强大&#xff0c;玩法也越来越多。最近&#xff0c;发现一款手机浏览器&#xff0c;竟然聚…

立创EDA入门3 通过51单片机最小板学习PCB设计

立创EDA入门3 通过51单片机最小板学习PCB设计一、本文目的二、原理图设计1. 新建工程&#xff0c;命名为51系统2. 各模块原理图3. 一些常用操作&#xff08;1&#xff09;放置普通元器件&#xff08;2&#xff09;封装、标签设置&#xff08;3&#xff09;在线库中查找元器件&a…

硬件系统工程师宝典(4)-----传输过程的信号要如何描述?

各位同学大家好&#xff0c;欢迎继续做客电子工程学习圈&#xff0c;今天我们继续来讲这本书&#xff0c;硬件系统工程师宝典。上篇我们说到为实现信号的有效传输&#xff0c;需要保证信号波形的完整和信号时序的完整&#xff0c;并且知道了从时域、频域两个角度去分析信号。那…

高压功率放大器在诱发肌电运动阈值对比研究中的应用

实验名称&#xff1a;经颅磁声刺激与经颅超声刺激诱发肌电运动阈值的对比研究 研究方向&#xff1a;生物医学 测试目的&#xff1a; 在脑科学与神经科学研究中&#xff0c;物理刺激是目前应用最广泛的电磁刺激技术。该技术利用变化的磁场诱发感应电流进而对神经组织进行调控&am…

递归算法实例应用(一)

递归算法实例应用&#xff08;一&#xff09; 递归简笔 递归和普通函数调用一样&#xff0c;都是通过函数栈实现。 以斐波那契数列递归调用为例 递归时函数调用栈的进栈、出栈过程可以由上述图示直观的体现出来&#xff0c; 因此可以得出递归的几个作用&#xff1a; ​ …

玩转CodeQLpy之代码审计实战案例

0x01 背景介绍CodeQLpy是一款半自动化的代码审计工具&#xff0c;能有效提高代码审计的效率&#xff0c;目前项目仍处于测试阶段。项目地址https://github.com/webraybtl/CodeQLpy&#xff0c;在github主页有对应的安装和使用介绍&#xff0c;如图1.1所示。-t: 指定待扫描的源码…