Spring系列五:Spring怎么解决循环依赖

2023-11-08

15.说说循环依赖

什么是循环依赖?

Spring循环依赖

Spring 循环依赖:简单说就是自己依赖自己,或者和别的Bean相互依赖。

鸡和蛋

只有单例的Bean才存在循环依赖的情况,原型(Prototype)情况下,Spring会直接抛出异常。原因很简单,AB循环依赖,A实例化的时候,发现依赖B,创建B实例,创建B的时候发现需要A,创建A1实例……无限套娃,直接把系统干垮。

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

Spring不支持基于构造器注入的循环依赖,但是假如AB循环依赖,如果一个是构造器注入,一个是setter注入呢?

看看几种情形:

循环依赖的几种情形

第四种可以而第五种不可以的原因是 Spring 在创建 Bean 时默认会根据自然排序进行创建,所以 A 会先于 B 进行创建。

所以简单总结,当循环依赖的实例都采用setter方法注入的时候,Spring可以支持,都采用构造器注入的时候,不支持,构造器注入和setter注入同时存在的时候,看天。

16.那Spring怎么解决循环依赖的呢?

PS:其实正确答案是开发人员做好设计,别让Bean循环依赖,但是没办法,面试官不想听这个。

我们都知道,单例Bean初始化完成,要经历三步:

Bean初始化步骤

注入就发生在第二步,属性赋值,结合这个过程,Spring 通过三级缓存解决了循环依赖:

  1. 一级缓存 : Map<String,Object>singletonObjects,单例池,用于保存实例化、属性赋值(注入)、初始化完成的 bean 实例

  2. 二级缓存 : Map<String,Object>earlySingletonObjects,早期曝光对象,用于保存实例化完成的 bean 实例

  3. 三级缓存 : Map<String,ObjectFactory<>>singletonFactories,早期曝光对象工厂,用于保存 bean 创建工厂,以便于后面扩展有机会创建代理对象。

三级缓存

我们来看一下三级缓存解决循环依赖的过程:

当 A、B 两个类发生循环依赖时:

A实例的初始化过程:

  1. 创建A实例,实例化的时候把A对象放三级缓存,表示A开始实例化了,虽然我这个对象还不完整,但是先曝光出来让大家知道

    1

  2. A注属性时,发现依赖B,此时B还没有被创建出来,所以去实例化B

  3. 同样,B注属性时发现依赖A,它就会从缓存里找A对象。依次从级到三级缓存查询A,从三级缓存通过对象拿到A,发现A虽然不太完善,但是存在,把A放级缓存,同时删除三级缓存中的A,此时,B已经实例化并且初始化完成,把B放入级缓存。

    2

  4. 接着A继续属性赋值,顺利从级缓存拿到实例化且初始化完成的B对象,A对象创建也完成,删除级缓存中的A,同时把A放级缓存

  5. 最后,级缓存中保存着实例化、初始化都完成的A、B对象

5

所以,我们就知道为什么Spring能解决setter注入的循环依赖了,因为实例化和属性赋值是分开的,所以里面有操作的空间。如果都是构造器注入的化,那么都得在实例化这一步完成注入,所以自然是无法支持了。

17.为什么要三级缓存?级不吗?

不行,主要是为了成代理对象。如果是没有代理的情况下,使用二级缓存解决循环依赖也是OK的。但是如果存在代理,三级没有问题,二级就不行了。

因为三级缓存中放的是成具体对象的匿名内部类,获取Object的时候,它可以成代理对象,也可以返回普通对象。使三级缓存主要是为了保证不管什么时候使的都是个对象。

假设只有级缓存的情况,往级缓存中放的显示个普通的Bean对象,Bean初始化过程中,通过 BeanPostProcessor 去成代理对象之后,覆盖掉级缓存中的普通Bean对象,那么可能就导致取到的Bean对象不一致了。

二级缓存不行的原因

18.@Autowired的实现原理?

实现@Autowired的关键是:AutowiredAnnotationBeanPostProcessor

在Bean的初始化阶段,会通过Bean后置处理器来进行一些前置和后置的处理。

实现@Autowired的功能,也是通过后置处理器来完成的。这个后置处理器就是AutowiredAnnotationBeanPostProcessor。

  • Spring在创建bean的过程中,最终会调用到doCreateBean()方法,在doCreateBean()方法中会调用populateBean()方法,来为bean进行属性填充,完成自动装配等工作。

  • 在populateBean()方法中一共调用了两次后置处理器,第一次是为了判断是否需要属性填充,如果不需要进行属性填充,那么就会直接进行return,如果需要进行属性填充,那么方法就会继续向下执行,后面会进行第二次后置处理器的调用,这个时候,就会调用到AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues()方法,在该方法中就会进行@Autowired注解的解析,然后实现自动装配。

    /**
    *?属性赋值
    **/
    protected?void?populateBean(String?beanName,?RootBeanDefinition?mbd,?@Nullable?BeanWrapper?bw)?{
    ????????????//…………?
    ????????????if?(hasInstAwareBpps)?{
    ????????????????if?(pvs?==?null)?{
    ????????????????????pvs?=?mbd.getPropertyValues();
    ????????????????}
    
    ????????????????PropertyValues?pvsToUse;
    ????????????????for(Iterator?var9?=?this.getBeanPostProcessorCache().instantiationAware.iterator();?var9.hasNext();?pvs?=?pvsToUse)?{
    ????????????????????InstantiationAwareBeanPostProcessor?bp?=?(InstantiationAwareBeanPostProcessor)var9.next();
    ????????????????????pvsToUse?=?bp.postProcessProperties((PropertyValues)pvs,?bw.getWrappedInstance(),?beanName);
    ????????????????????if?(pvsToUse?==?null)?{
    ????????????????????????if?(filteredPds?==?null)?{
    ????????????????????????????filteredPds?=?this.filterPropertyDescriptorsForDependencyCheck(bw,?mbd.allowCaching);
    ????????????????????????}
    ????????????????????????//执行后处理器,填充属性,完成自动装配
    ????????????????????????//调用InstantiationAwareBeanPostProcessor的postProcessPropertyValues()方法
    ????????????????????????pvsToUse?=?bp.postProcessPropertyValues((PropertyValues)pvs,?filteredPds,?bw.getWrappedInstance(),?beanName);
    ????????????????????????if?(pvsToUse?==?null)?{
    ????????????????????????????return;
    ????????????????????????}
    ????????????????????}
    ????????????????}
    ????????????}
    ???????????//…………
    ????}
    
  • postProcessorPropertyValues()方法的源码如下,在该方法中,会先调用findAutowiringMetadata()方法解析出bean中带有@Autowired注解、@Inject和@Value注解的属性和方法。然后调用metadata.inject()方法,进行属性填充。

    ????public?PropertyValues?postProcessProperties(PropertyValues?pvs,?Object?bean,?String?beanName)?{
    ????????//@Autowired注解、@Inject和@Value注解的属性和方法
    ????????InjectionMetadata?metadata?=?this.findAutowiringMetadata(beanName,?bean.getClass(),?pvs);
    
    ????????try?{
    ????????????//属性填充
    ????????????metadata.inject(bean,?beanName,?pvs);
    ????????????return?pvs;
    ????????}?catch?(BeanCreationException?var6)?{
    ????????????throw?var6;
    ????????}?catch?(Throwable?var7)?{
    ????????????throw?new?BeanCreationException(beanName,?"Injection?of?autowired?dependencies?failed",?var7);
    ????????}
    ????}
    

先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

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

Spring系列五:Spring怎么解决循环依赖 的相关文章

  • PhoneGap/Cordova 应用程序通知

    我是 PhoneGap Cordova 的新手 我希望向我的应用程序添加一些通知 推送通知 因此当应用程序上发布新文章时 它会提醒用户 本地通知 在设定的时间间隔 日期和时间 我可以提示用户我的应用程序上的最新文章 我进行了大量搜索 但找不
  • 在根项目“bin”中找不到任务“bintrayUpload”

    我遵循了将 AAR 文件发布到 JCenter 所需的每个步骤 然后使用 Maven Central 将其同步https github com danielemaddaluno gradle jcenter publish https gi
  • 如何在 Android 中更改 Drawable 的颜色?

    我正在开发一个 Android 应用程序 并且我有一个从源图像加载的可绘制对象 在此图像上 我想将所有白色像素转换为不同的颜色 例如蓝色 然后缓存生成的 Drawable 对象 以便稍后使用它 举例来说 假设我有一个 20x20 PNG 文
  • 应用程序在加载 xml 布局文件的主线程中做了太多工作

    我正在制作一个 9x9 数独网格 其中 81 个单元格本身就是一个 3x3 网格 单个细胞看起来像这样 1 2 3 4 5 6 7 8 9 每个数字代表该单元格的铅笔注释 我有一个名为 cell layout xml 的文件 表示这种 3x
  • 如何从Firebase Firestore实时更新文档中获取修改后的字段或数据? [复制]

    这个问题在这里已经有答案了 我有多个文档 我的问题是我无法获取修改的特定数据 我正在获取完整的文档 db collection employees whereEqualTo OID OID addSnapshotListener new E
  • 使用 Fragment 在工具栏中实现 SearchView

    当前情况 我的应用程序主页由导航抽屉组成 因此我将视图作为片段加载 我的工具栏中也有搜索图标 我在中实现了它menu xml 下一步我实施了SearchView通过以下问题的答案来获取搜索图标在工具栏中实现搜索 https stackove
  • 当满足条件时,如何以编程方式更改 ImageButton src 目标?

    我有一个学校项目 我正在尝试开发一个手电筒应用程序 对于开 关 ImageButton 我想要 4 个自定义图像 如果手电筒关闭 turn on png 默认 turn on pressing png 按下状态 true 如果手电筒打开 t
  • 吉夫伦致命信号11

    我正在尝试使用一些本机代码来创建 Gif 我使用绘画绘制图像 创建一些笔画 单击 保存 绘制的图像将保存为 JPG 格式 当我单击 创建 Gif 时 它会获取所有图像并开始创建 gif 这是当我收到致命信号 11 并且应用程序重新启动时 我
  • 为什么反射会减慢Android手机的速度

    我多次读到反射会降低手机性能 这有多真实 例如 在我的例子中 我从 Web 服务获取一些参数 这些参数与我在 Android 应用程序中的类的参数同名 所以我只是使用java字段和反射设置这些参数的值 它似乎并没有降低性能 有人可以向我解释
  • 退出设备上的 system.img

    我正在为我们部署给客户的设备 LG p509 Optimus 1 开发自动应用程序更新解决方案 我们可以控制这些设备 并且目前在它们上安装自定义内核 但不是完整的自定义 ROM 由于我们试图在设备上自动更新我们的应用程序 因此我们需要由我们
  • 需要 Android webview window.open() 和 window.close() 的信息

    我正在开发一个安卓应用程序 这是我网站的 WebView 该网站包含一个弹出按钮 单击该按钮后 将打开一个新窗口并显示内容 该链接可以来自外部站点 然而 当我实现此操作时 新选项卡正在打开 之后它会弹出以打开浏览器 尽管在 Web 视图中打
  • Android 中 localTime 和 localDate 的替代类有哪些? [复制]

    这个问题在这里已经有答案了 我想使用从 android API 获得的长值 该值将日期返回为长值 表示为自纪元以来的毫秒数 我需要使用像 isBefore plusDays isAfter 这样的方法 Cursor managedCurso
  • 将 java 中的 byte[] 转换为 C++ 中的 unsigned char* 的正确方法,反之亦然?

    我是 C 和 JNI 的新手 我尝试找到一种正确的方法 通过使用 JNI 将 java 中的 byte 转换为 C 中的 unsigned char 反之亦然 我正在安卓上工作 在谷歌和SO中寻找解决方案后 我还没有找到将java中的byt
  • Android -room 持久库 - DAO 调用是异步的,因此如何获取回调?

    从我读到的Room 不允许您在主线程上发出数据库查询 因为可能会导致主线程延迟 所以想象一下我正在尝试更新 UI 主线程上的文本视图 其中一些数据我将如何得到回调 让我给你举个例子 想象一下 我想将我的业务模型数据存储到一个名为 事件 的对
  • 当 minifyEnabled 为 true 时 Android 应用程序崩溃

    我正在使用多模块应用程序 并且该应用程序崩溃时minifyEnabled true in the installed模块的build gradle 以下是从游戏控制台检索到的反混淆堆栈跟踪 FATAL EXCEPTION Controlle
  • 如何通过 Android 按钮单击运行单独的应用程序

    我尝试在 Android 应用程序中添加两个按钮 以从单独的两个应用程序订单系统和库存系统中选择一个应用程序 如图所示 我已将这两个应用程序实现为两个单独的 Android 项目 当我尝试运行此应用程序时 它会出现直到正确选择窗口 但是当按
  • 无法使用 findViewById() 找到视图

    我找不到TextView通过致电findViewById 即使 ID 确实存在 OtherActivity public class OtherActivity extends Activity Override protected voi
  • Unity c# 四元数:将 y 轴与 z 轴交换

    我需要旋转一个对象以相对于现实世界进行精确旋转 因此调用Input gyro attitude返回表示设备位置的四元数 另一方面 这迫使我根据这个四元数作为默认旋转来计算每个旋转 将某些对象设置为朝上的简单方法如下 Vector3 up I
  • jetpack compose 是否使用drawable-night 文件夹?

    我们有一个基于视图的 Android 应用程序 其中有一些可绘制对象res drawable文件夹 以及夜间模式的对应文件夹res drawable night folder 使用旧视图时 引用可绘制对象R drawable foo从 XM
  • 检查应用程序是否在 Android Market 上可用

    给定 Android 应用程序 ID 包名称 如何以编程方式检查该应用程序是否在 Android Market 上可用 例如 com rovio angrybirds 可用 而 com random app ibuilt 不可用 我计划从

随机推荐

  • 企业知识分享系统的设计与实现

    摘 要 随着信息技术和网络技术的飞速发展 人类已进入全新信息化时代 传统管理技术已无法高效 便捷地管理信息 为了迎合时代需求 优化管理效率 各种各样的管理系统应运而生 各行各业相继进入信息管理时代 企业知识分享系统就是信息时代变革中的产物之
  • 基于AI的4G/5G基站节能的智能解决方案

    随着移动通信网络建设规模逐年增加 通信设备对能源的需求与日俱增 移动通信网络的能耗在运营商的运营成本 OPEX Operating Expense 占比已高于15 经过5G试商用网络的测试验证 5G单站功耗是4G单站功耗的3 4倍 运营商面
  • 提示OpenGL版本过低怎么办

    OpenGL是一个可以加速2D和3D图形的图形库 在计算机显示技术中广泛使用 常用于游戏制作 建筑设计 医疗成像 科学数据可视化等领域 然而 当你尝试运行使用OpenGL的软件或游戏时 你可能会收到一个消息 OpenGL版本过低 请升级驱动
  • 易语言升级版火山软件开发平台现在很庞大了

    中文编程的魅力很吸引人 易语言时代就经常用它编编小程序 易语言最后是输出成vc6编译出来的效果一样 小而精 vc6毕竟是比较古老的技术 现在升级版火山软件开发平台已经能够比较耐用了 一个ide可以开发安卓和windows这2种应用 wind
  • BlueZ5.45 D-Bus总线 GATT API 分析

    笔者目前做linux系统下bluez蓝牙项目开发 发现网上关于bluez开发的资料很少 对于刚开始接触bluez蓝牙的开发人员来说是非常痛苦的 通过调试bluez源码自带的应用例子和文档说明 对BlueZ5 45 D Bus总线 GATT
  • 最大公约数GCD

    输入2个正整数A B 求A与B的最大公约数 Input2个数A B 中间用空格隔开 1 lt A B lt 10 9 Output输出A与B的最大公约数 Sample Input 30 105 Sample Output 15 includ
  • 对于长度为5位的一个01串,每一位都可能是0或1,一共有32种可能。

    对于长度为5位的一个01串 每一位都可能是0或1 一共有32种可能 它们的前几个是 00000 00001 00010 00011 00100 请按从小到大的顺序输出这32种01串 输入格式 本试题没有输入 输出格式 输出32行 按从小到大
  • Unity3D中三种调用其他脚本函数的方法

    第一种 被调用脚本函数为static类型 调用时直接用 脚本名 函数名 第二种 GameObject Find 脚本所在的物体的名字 SendMessage 函数名 能调用public和private类型函数 第三种 GameObject
  • 14-矩阵相乘及其运算法则

    矩阵与向量的乘法 在这一篇文章中我们就将基于上一篇重新审视矩阵的这个视点来理解矩阵的乘法 那么在这一篇 我们主要来看一下矩阵和向量的乘法 这里这个线性方程组是上一小节给大家举的模拟的一个非常简单的小型经济系统的例子 我们可以把这个经济系统其
  • 细说业务逻辑(后篇)

    细说业务逻辑 后篇 作者 EricZhang T2噬菌体 来源 博客园 时间 2009 11 01 阅读 295 次 原文链接 收藏 前篇 http kb cnblogs com page 50470 3 业务逻辑的架构模式及实现 Mart
  • Unity3d之Animation(动画系统)

    Unity3d之Animation 动画系统 原创 2016年04月30日 18 15 17 标签 unity3d 动画 Animation 20914 1 动画系统配置 2 代码控制动画 原文地址 http blog csdn net d
  • Windows C++远程线程(CreateRemoteThread)注入DLL方法、代码示例。

    使用远程线程CreateRemoteThread的方法可以在其他进程中注入自己想注入的DLL 千万不要用这个方法搞破坏哦 我们搞一个可以弹窗的DLL代码 让进程只要LOAD DLL就会弹窗 DLL代码 include
  • 第16节 综合实验—部署域,在域中部署DHCP、WEB和文件共享服务器

    综合实验 1 共享服务器在域环境中与在工作组中的区别 1 1 在工作组中时 1 2 在域中时 2 实验要求 3 实验步骤 3 1 构建域与配置DNS服务器 用win2008 3 2 将win2003构建成DHCP WEB以及文件共享服务器
  • Python连接Mysql数据库,读写数据库中图片

    本文使用到的pymysql为1 0 2版本 在IDEA中配置Python参考本人写的另一篇文章http t csdn cn OHCRC 第一步 安装PyMySQL PyMySQL用于 python连接到MySQL数据库服务器 在IDEA中安
  • Axiom3D:Ogre地形代码解析

    Axiom3D Ogre地形代码解析 作者 天天不在 发布日期 2014 09 01 20 52 05 我来说两句 0 Tag标签 地形 代码 大致流程 这里简单介绍下 Axiom中采用的Ogre的地形组件的一些概念与如何生成地形 先说下大
  • mysql查看数据类型命令_mysql菜鸟指南(增删查改、数据类型、常用命令)

    1 连接数据库 root host mysql u root p Enter password 2 查看数据库 SHOW DATABASES 3 创建数据库 mysql gt CREATE DATABASE 库名 mysql gt USE
  • 面试题目解析-----三极管&窗口比较器

    第4题 面试大佬直接给出了答案 有题目可以知道题目设计要求是正逻辑 3 3V输入12V输出 0V输入0V输出 使用两个NPN管子就可以实现 第5题 设计一个窗口比较器输出两种不同的状态 并点亮LED 答案如下 第5题更新一个优化版 优化版本
  • 行业变天,汽车经销商如何转型破局?

    2018年 对于中国汽车经销集团来说 可谓是一个分水岭 首次迎来了数月月度销量同比降幅超过两位数的时代 行业转型迫在眉睫 在11月初举行的汽车流通协会年会中 汽车经销集团们确立了新的建设目标和方向 会议上 协会会长沈进军以及诸多行业企业代表
  • Spring Security + token前后端分离该怎么认证

    前言 因为这个Spring Security学习的过程比较曲折 最初以为比较简单 但是实际上也确实比较简单 最大的坑点在于 大多数找到的关于Spring Security都不是基于前后端分离进行的配置 解决了一个bug发现了更多的bug 烦
  • Spring系列五:Spring怎么解决循环依赖

    15 说说循环依赖 什么是循环依赖 Spring循环依赖 Spring 循环依赖 简单说就是自己依赖自己 或者和别的Bean相互依赖 鸡和蛋 只有单例的Bean才存在循环依赖的情况 原型 Prototype 情况下 Spring会直接抛出异