Spring 循环依赖的三级缓存

2023-11-15

在Spring Bean 的生命周期中,里面有一步就是填充属性。而填充属性之前会判 属性对象是否被当前对象循环依赖,当发现属性对象被循环依赖的时候会进行aop(被命中)并且生成属性对象的代理对象(未命中目标对象)。

循环依赖是如何形成的
在这里插入图片描述
当 对象UserA 实例化完成,进行填充属性UserB 的时候 ,先去单例池里面去获取 UserB 对象,初次没有获取到,开始实例化UserB ,当UserB 实例化完成,进行填充属性UserA 的时候,先去单例池里面去获取 UserA 对象,初次没有获取到,再去实例化UserA ,并进行填充属性UserB,此时造成死循环。

单例池 存放的是经过完成生命周期的对象即最终的产出的SpringBean。

如何解决循环依赖
我们先回顾一下Spring Bean生命周期的大致步骤 ,如下图
在这里插入图片描述

如何解决呢
1.添加一个缓存MapA,如图所示
在这里插入图片描述
当实例化UserA 的时候 先将实例化好的UserA 对象放到 MapA 中,再来填充它的UserB 属性,单例池里面没有,去实例化UserB ,并将实例化好的UserB 对象放到 MapA 中,再来填充 UserB对象的 UserA属性,单例池里面没有,去缓存 MapA 里面获取并且获取到了,直接给属性UserA 赋值,UserB 创建完成,将UserB放入单例池,再将 UserA对象 的 UserB 属性赋值,再完成UserA 的创建,最后将UserA 放入单例池。

但是根据上面的生命周期而言,循环依赖赋值的属性对象值并不是最终的那个Spring Bean ,只是一个实例对象。

就是说这个缓存里面放的并不是最终的对象(虽然内存里面都是同一份地址,但是这个对象可能被AOP表达式命中的对象呢)

那么如果 MapA 里面存放的是实例化或者是被代理的对象呢?
那次此时 放入 MapA 缓存之前还需要判断 这个对象是否需要进行代理。如果需要进行代理则存放代理对象,如果不需要代理,则存放原始目标对象即当前的实例化对象。

如下图
在这里插入图片描述
这样最终的放入单例池或者属性赋值的对象就是一个完整的Spring Bean了。

这样一来又有一个问题。

如何判断这个实例化的对象需要提前进行代理?

(因为正常流程生成AOP对象是在初始化之后干的事)

发生循环依赖的对象(被aop 表达式命中的对象产出循环依赖也会提前)
那又如何判断当前正在被创建的对象产生了循环依赖呢

如下图所示
在这里插入图片描述
当正在创建UserA对象的时候,将当前正在被创建的UserA对象放入 setA 集合中。当UserA被实例化出来,填充UserB属性,去setA 集合判断有没有,再创建UserB 对象,再填充UserB 对象的UserA 属性时,去serA 集合判断有没有,如果有即代表UserA被循环依赖了。

如下图所示
在这里插入图片描述
那么这样是不是就解决了循环依赖的问题呢?

我们刚才的场景是这样的
在这里插入图片描述
那如果又依赖了UserC 呢
在这里插入图片描述
当 UserA 和 UserB 发生了循环依赖问题,我们生成了一个UserA 的代理对象。那如果此时 UserA 和 UserC 也发生了循环依赖。当UserA 对象里面的UserB 属性填充完成,再继续填充UserC 的时候是不是也会生成另外一个新的代理对象了。那最终存入单例池的那个最终的User的代理对象就不是同一份呢。
当然这个问题其实在在缓存里面判断一下UserA的代理对象是否存在即可解决问题。

但是 Spring 在解决循环依赖的时候使用了三层缓存。我们当前已知的缓存只有两个 第一个单例池 和 第二个 MapA。那第三个怎么来的呢?

继续看这个图
在这里插入图片描述
从实例化UserA开始到下面的UserB里面填充UserA属性,再生成UserA的代理对象,那生成UserA代理对象的目标对象怎么得到?源码中并没有把实例化好的原始UserA对象当作一个参数去导出传啊,此时加入第三个集合 MapB 就是 形成了Spring 解决循环依赖的三个集合了。

在这里插入图片描述

Spring 解决循环依赖的三级缓存
1.singletonObjects --单例池
2.earlySingletonObjects – MapA
3.singletonFactories – MapB

单例池存放的都是最终的经过完整生命周期的Spring Bean 对象
MapA 存放的是目标对象或者代理对象
MapB 存放的是实例化的原始对象即目标对象

singletonsCurrentlyInCreation setA

源码跟踪地址:循环依赖源码流程

共勉

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

Spring 循环依赖的三级缓存 的相关文章

随机推荐

  • curl命令介绍与使用

    curl 全称CommandLine URL 或 CommandLine Uniform Resource Locator 顾名思义 curl命令是在命令行方式下工作 利用URL的语法进行数据的传输或者文件的传输 这个命令行工具现在多用于U
  • 基于Python Django Mysql 开发的宠物用品商城

    最近做的一个程序设计 核心是基于django做一个商城 功能包含登录 注册 商品浏览 购物车 支付模块 支付宝沙箱支付 评价 轮播图 开发环境 Python版本 3 8 7 Django版本 3 1 5 数据库版本 mysql8 数据库管理
  • Hive(二):获取HiveConnection,及重现获取过程中的某些问题。

    为什么80 的码农都做不了架构师 gt gt gt 一 未创建Maven工程 所需jar包都是自己一一找的 结果碰到许多缺包问题 最后我的lib下包为 二 尝试获取HiveConnection 先附上成功获取HiveConnection的代
  • nexus3 Unauthorized问题解决

    环境 Nexus3 version 3 20 1 01 问题 nexus3 在安装完后通过mvn deploy命令 出行 Return code is 401 ReasonPhrase Unauthorized 异常 分析解决 1 mave
  • Hadoop集群启动后,在web:50070端口只有两个datanode节点

    spark集群部署规划 hadoop1 master worker datanode namenode secondarynamenode hadoop resourcemanager nodemanager yarn hadoop2 wo
  • 使用AD14制作PCB的全部流程以及PCB打样流程介绍

    文章目录 1 各PCB打样公司 1 1 深圳嘉立创 1 2 捷配 1 3 华秋 1 4 猎板 2 嘉立创PCB打样流程 2 1 在线下单 2 2 上传PCB文件 2 3 选择板子数量 2 4 工艺参数设置 3 AD元器件库 4 AD14创建
  • 上手Web自动化测试(Python+selenium+unittest)

    从安装库 包和文件配置上手Web自动化测试 Python主要用selenium和unittest库 此外还有用于远程测试的Selenium Server包 浏览器需要配置chromedriver 1 安装selenium库 可以在Pycha
  • Robot Framework (分层实现UI自动化)

    更多资源请前往博主个人网站 http www qabujiaban com Robot Framework介绍与安装 目录 Robot Framework介绍与安装 Robot Framework是什么 什么是自动化测试框架 Robot F
  • linux Ubuntu 引导,重建ubuntu的grub开机引导

    这里将告诉您重建ubuntu的grub开机引导 具体实现方法 pc开机时 在进入系统之前 要先进入的磁盘里安装了grub开机引导的区域 如果是单系统一般不会有问题 但若是多系统像win ubuntu或者ubuntu ubuntu等 有时会出
  • VS2019+Qt Release模式下显示打印信息

    使用VS作为Qt的开发工具 在Debug模式下开发 软件的运行效率不如Release模式下高 所以经常会出现在Release模式下 查看程序的打印输出 只需要在项目属性配置一下即可 点击项目右键 属性 生成事件 生成后事件 在命令行里添加下
  • HJ212-Pack HJ212协议组包实现

    HJ212 Pack HJ212协议组包实现 仓库地址 https gitee com ll0 0ll HJ212 Pack HJ212协议认识 https blog csdn net lblmlms article details 108
  • STM32定时器----TIMx->SR寄存器

    在研究stm32定时器时发现 当进入定时器的中断时 定时器计数器以及中断接收时正常运行的 并没有因为进入中断而停止 具体看以下分析 定时器初始化代码 TIM捕获通道开启宏定义 1 开启 0 关闭 define TIM ICCH1 1 def
  • Vite热更新不起作用

    错误示例 path show component gt import views Show index vue show 目录名 注意路由中是大写开头 这里是小写的 这就是问题所在 Index vue 文件名 这样写目录名或文件名与路由中写
  • Windows10 搭建vs2017OpenGL ES 3.0 开发环境

    前言 更新于2021 06 29 1 首先下载 OpenGL ES 3 0 Programming Guide 随书源码 直接使用CMake工具生成vs2017代码工程 然后使用vs2017打开工程 ES3 Book sln 然而 vs报错
  • mysql多对一、多对多查询实践

    最近做的功能涉及这方面 算是练了一下 首先多对一部分较简单 多的那一方表 多设置一个字段是少的那一方的id 主键 具体查询时候关联查询即可 可设置外键进行级联操作 具体以后做到这个功能再更新 多对多 设备和用户多对多 一个用户可有多台设备
  • 用java编写赛马_java applet 赛马小程序

    昨天写看java书的时候 觉得闷 就写了个小程序自我娱乐一下 技术含量不高 只是自我娱乐而已 下面的代码只要编译出class文件 再写一个HTML格式的文件 调用Arc2Demo2 class 就能看了 该程序实现一个跑马比赛程序 各个选手
  • Translucent System Bar 的最佳实践

    Translucent System Bar 的最佳实践 http mp weixin qq com s biz MzA3MDMyMjkzNg mid 2652262235 idx 1 sn f7ebd354b2ad04064d27e0df
  • 【Docker】基于docker构建深度学习开发环境

    文章目录 1 选择基础镜像 1 1 不同深度学习框架对CUDA和cuDNN的要求 1 2 显卡驱动对CUDA的要求 1 3 深度学习框架对CUDA不同版本要求冲突的解决 2 创建容器并进行基础配置 2 1 创建容器并查看CUDA和cuDNN
  • 某网站面试算法题,今天碰到了,两个大数字相加(足够大)取和

    两个大数相加 1 是整数 2 两个数无限大 long都装不下 3 不能用BigInteger 4 不能用任何包装类提供的运算方法 5 两个数都是以字符串的方式提供 面试答卷直接写下 此处空白位置太小 写不下 请求机试 果然给我一台应该是 爬
  • Spring 循环依赖的三级缓存

    在Spring Bean 的生命周期中 里面有一步就是填充属性 而填充属性之前会判 属性对象是否被当前对象循环依赖 当发现属性对象被循环依赖的时候会进行aop 被命中 并且生成属性对象的代理对象 未命中目标对象 循环依赖是如何形成的 当 对