Spring源码解析7-bean循环依赖

2023-05-16

循环依赖

 

 

普通对象的循环依赖

1、先实例化A对象,未初始化,半成品 A@1536

2、将实例化A对象的lambda表达式放入三级缓存

3、A对象填充b属性值,b是一个RuntimeBeanReference

4、从容器中的缓存取B对象,取不到,创建实例化B对象,半成品 B@1937

5、将实例化B对象的lambda表达式放入三级缓存

6、B对象填充a属性值,a也是一个RuntimeBeanReference,尝试从容器的1,2,3缓存中依次取key为a的beanName,从三级缓存中取到key为a,value是lambda表达式

7、调用singletonFactory.getObject(),函数式接口的唯一方法,进去lambda表达式的方法,getEarlyBeanReference(beanName, mbd, bean)获取到半成品的A对象 A@1536

8、将半成品的 A@1536 放入二级缓存,将三级缓存中k:a—v:lambda表达式 移除,然后将半成品A对象 填充到B对象中的a属性

9、此时B对象是成品对象,放入一级缓存中,把二,三级缓存中bean为b的移除

9、填充A对象中b属性值,此时A对象也是成品对象,放入一级缓存中,把二,三级缓存中bean为a的移除

循环依赖的对象,不需要代理的话,只需要二级缓存可以解决所有问题,但是当存在代理之后就无法解决了,必须要使用三级缓存来解决

AOP 代理对象的生成是在成品对象创建完成之后,在获取具体的对象的时候,直接通过lambda表达式动态生成对应的代理对象,也就是通过BPP的after()方法创建的,

普通对象会被代理对象覆盖,最终放入一级缓存

AOP代理对象的循环依赖

1、先实例化A对象,未初始化,半成品 A@1536

2、将实例化A对象的lambda表达式放入三级缓存

3、A对象填充b属性值,b是一个RuntimeBeanReference

4、从容器中的缓存取B对象,取不到,创建实例化B对象,半成品 B@1937

5、将实例化B对象的lambda表达式放入三级缓存

6、B对象填充a属性值,a也是一个RuntimeBeanReference,尝试从容器的1,2,3缓存中依次取key为a的beanName,value是lambda表达式

7、调用singletonFactory.getObject(),接口的唯一方法,进去lambda表达式的方法,getEarlyBeanReference(beanName, mbd, bean)获取到半成品的A对象 A@1536,经过BeanPostProcessor的after生成代理A对象

8、将A代理对象放入二级缓存,将三级缓存中k:a—v:lambda表达式 移除,然后将代理A对象 填充到B对象中的a属性,B是成品对象,然后经过BeanPostProcessor的after生成代理B对象

9、此时代理B对象是成品对象,放入一级缓存中,把二,三级缓存中bean为b的移除

10、填充A对象中b属性值,此时A对象也是成品对象,放入一级缓存中,把二,三级缓存中bean为a的移除

第一级缓存:对外暴露的对象,属性已填充的完整对象

第二级缓存:为了处理循环依赖的对象创建问题,存的是半成品对象或半成品对象的代理对象

第三级缓存:处理存在 AOP + 循环依赖的对象创建问题,能将代理对象提前创建,提前暴露的对象,存放已经创建完成但还没有注入好的对象的工厂对象,通过这个工厂可以返回代理对象

Spring 是如何解决循环依赖的问题的

三级缓存,提前暴露对象,aop

总:循环依赖问题,A依赖B,B依赖A

分:先说明bean的创建过程:实例化,初始化(填充属性)

​1、先创建A对象,实例化A对象,此时A对象中的b属性为空,填充属性b

​2、从容器中查找B对象,如果找到了,直接赋值不存在循环依赖问题(不通),找不到直接创建B对象

​3、实例化B对象,此时B对象中的a属性为空,填充属性a

​4、从容器中查找A对象,找不到,直接创建

​形成闭环的原因:

​A对象是存在的,此时的A对象不是一个完整的状态,只完成了实例化但未完成初始化,可以优先把非完整状态的对象优先赋值,等待后续操作来完成赋值,相当于提前暴露了某个不完整对象的引用,解决问题的核心在于实例化和初始化分开操作,是解决循环依赖问题的关键。

​当所有的对象都完成实例化和初始化操作之后,还要把完整对象放到容器中,此时在容器中存在对象的几个状态,完成实例化但未完成初始化状态与完整状态,所以需要不同的map结构来进行存储,

就有了一级缓存和二级缓存,如果一级缓存中有了,那么二级缓存中就不会存在同名的对象,因为查找顺序是1,2,3这样的方式来查找的。一级缓存中放的是完整对象,二级缓存中放的是非完整对象。

​为什么需要三级缓存?三级缓存的value类型是ObjectFactory,是一个函数式接口,存在的意义是保证在整个容器的运行过程中同名的bean对象只能有一个。

​如果一个对象需要被代理或需要生成代理对象,要优先生成一个普通对象。

​普通对象和代理对象是不能同时出现在容器中的,因此当一个对象需要被代理的时候,就要用代理对象覆盖掉之前的普通对象,在实际的调用过程中,是没有办法确定什么时候对象被使用,所以当某个对象被调用的时候,优先判断此对象是否需要被代理,类似于一种回调机制的实现,可以通过lambda表达式来执行对象的覆盖过程,getEarlyBeanReference()。

​因此,所有的bean对象在创建的时候都要优先放到三级缓存中,在后续的使用过程中,如果需要被代理则返回代理对象,如果不需要被代理,则直接返回普通对象

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring源码解析7-bean循环依赖 的相关文章

  • ubuntu 文件夹消失怎么办

    点击文件夹 xff0c 然后摁F9
  • ffmpeg花屏解决(修改源码,丢弃不完整帧和解码错误帧)

    linux下模拟丢帧的命令 因为帧之间的参考关系 实测如果是1 几乎没有完好的帧 tc只能对发出的包做处理 但它还可以做延时抖动处理 sudo tc qdisc add dev enp0s31f6 root netem loss span
  • 使用SDF对ROS-Gazebo中模型进行编辑修改

    什么是SDF文件 SDF是一种XML格式 xff0c 能够描述机器人 静态和动态物体 照明 地形甚至物理学的各方面的信息 SDF可以精确描述机器人的各类性质 xff0c 除了传统的运动学特性之外 xff0c 还可以为机器人定义传感器 表面属
  • Ubuntu16.04桌面图标消失

    项目场景 xff1a 提示 xff1a 这里简述项目相关背景 xff1a 例如 xff1a Ubuntu16 04用了挺久 问题描述 xff1a 提示 xff1a 这里描述项目中遇到的问题 xff1a Ubuntu16 04重启后 xff0
  • uni-app 半屏地图拖拽滚动底部半屏,仿高德搜索拖拽

    lt template gt lt view class 61 34 event help details 34 gt lt view class 61 34 back last page 34 style 61 34 39 top 39
  • 1、 Seata快速开始

    一 seata服务搭建 第一步 xff1a 下载seata安装包 我们通过https github com alibaba spring cloud alibaba wiki E7 89 88 E6 9C AC E8 AF B4 E6 98
  • CSDN-markdown编辑器(含源码.md文件)

    说明 CSDN新版Markdown编辑器 20190412 这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题 xff0c 有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适
  • PS如何调整图片的大小

    快捷键Ctrl 43 Alt 43 i 一般会把纵横比勾选上
  • JAVA输入一个四位数的整数,要求编程将这个四位数中的个位,十位,百位,千位分别输出

    4 输入一个四位数的整数 xff0c 要求编程将这个四位数中的个位 xff0c 十位 xff0c 百位 xff0c 千位分别输出 1234 1 2 3 4 System out println 34 请输入四位数整数 xff1b 34 Sc
  • JAVA托运行李计算费用

    7 托运行李计算费用 xff1a 实验要求 xff1a xff08 1 xff09 货车在计算托运行李费用时以kg为单位计算费用 xff08 12元 kg xff09 xff0c 忽略重量中的小数部分 xff0c 即忽略不足1kg的部分 x
  • JAVA复制数组的三种方法

    public class test01 ArraysCopy public static void main String args 1 用for循环复制数组 int arr 61 1 2 3 4 5 6 7 8 9 int arr1 61
  • 如何把已安装的nodejs高版本降级为低版本

    windows如何把已安装的nodejs高版本降级为低版本 第一步 xff1a 先清空本地安装的node js版本 1 按健win 43 R弹出窗口 xff0c 键盘输入cmd 然后敲回车 xff08 或者鼠标直接点击电脑桌面最左下角的wi
  • SQL版本:多表连接查询(两张表为例)

    SQL版本 xff1a 数据准备 xff1a 创建一个数据库company CREATE DATABASE IF NOT EXISTS company 创建部门表 CREATE TABLE dept id INT PRIMARY KEY A
  • 【Redis 常用五大数据类型】

    常用五大数据类型 官方获取redis常见数据类型操作命令 xff1a http www redis cn commands html 1 Redis键 key keys 查看当前库所有key 匹配 xff1a keys 1 exists k
  • 【Mysql 基础知识】

    一 引言 1 1 现有的数据存储方式有哪些 xff1f Java程序存储数据 xff08 变量 对象 数组 集合 xff09 xff0c 数据保存在内存中 xff0c 属于瞬时状态存储 文件 xff08 File xff09 存储数据 xf
  • vue3 + vite + ts + setup , 第九练 自定义指令directive的使用,简单封装一个拖动指令

    除了 Vue 内置的一系列指令 比如 v model 或 v show 之外 xff0c Vue 还允许你注册自定义的指令 xff0c 一个自定义指令被定义为一个包含类似于组件的生命周期钩子的对象 钩子接收指令绑定到的元素 1 Vue3指令
  • MyBatis框架知识点总结

    一 引言 1 1 什么是框架 xff1f 框架 xff1a 框架使用你的 xff0c 而不是你在使用框架的 框架让我们提供什么信息 xff0c 配置信息 xff0c 数据库连接用户名密码等 xff0c 你必须提供 xff0c 还得按照框架要
  • AndroidStudio Unresolved reference

    在学习Kotlin过程中 xff0c 出现了两次在activity main xml中已注册id xff0c 但是在MainActivity kt中无法找到该Button的情况 后面发现是没有在build gradle中导入 39 koti
  • Spring学习(全)

    本文目录 1 Spring概述2 IOC 控制反转2 1 简单介绍2 2 Spring的第一个程序2 3 DI入门2 3 1 XML之set注入简单类型的set注入引用类型的set注入引用类型的自动注入autowire 2 3 2 XML之
  • Python3读写dbf文本

    Python3读写dbf文本 安装环境 pip install dbf 关于dbf的文档可以在一下网址了解dbf文档 https pythonhosted org dbf 还有github的地址 https github com ethan

随机推荐