【CVTE 一面凉经Ⅰ】循环依赖如何解决

目录

  • 一.🦁 开始前的废话
  • 二. 🦁 什么是循环依赖?
  • 三. 🦁Spring 容器解决循环依赖的原理是什么?
  • 五. 🦁 三级缓存解决循环依赖的原理
  • 六. 🦁 由有参构造方法注入属性的循环依赖如何解决?
  • 七.🦁 ENDing

权限管理

一.🦁 开始前的废话

最近一个小伙伴"李四"面 C 厂后台开发凉凉了,原因是面试官就他简历的微服务项目问了个很常见的面试题,他没答上来!现在咱们来看看面试官和李华的对话:

面试官问道:“你的项目如果遇到循环依赖了咋办?”

李四见状惊喜万分,笑着回答,确实遇到过,官方推荐我在 yaml 文件添加这段代码来解决:

spring:main:allow-circular-references: true

面试官:“嗯呢,这个可以!这段代码什么意思?那如果是通过构造函数导入的实例,还能使用这段代码解决嘛?”

李四:我 😥…

面试官:“好的,没关系。回家等消息叭!”


经过面试官二连问,李华败下阵来。那我们现在来讨论一下这个循环依赖应该怎么解决!从源头开始剖析。

二. 🦁 什么是循环依赖?

其实循环依赖是指在我们 Coding 的过程中,由于不好的设计,导致两个或以上的 Bean 相互依赖导致形成了一个闭环(lion 依赖 tiger,反过来 tiger 也在依赖 lion)。在 Springboot 2.6 以前,spring 容器是可以在实例化 Bean 的过程中,是可以自动解决一部分循环依赖的问题(依靠三级缓存),由于这个方案导致了越来越多的 Coder 老是滥用,导致代码质量越来越差,所以从 SpringBoot 2.6 起,就默认把这种方案给禁用了。如果你的项目里还存在循环依赖,SpringBoot 将拒绝启动!

***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
|  tiger defined in file [/Users/study/personal-projects/easy-web/target/classes/com/log/web/controller/Lion.class]
↑     ↓
|  lion (field private com.log.web.controller.Tiger com.log.web.service.Tiger.class)
└─────┘

需要我们在 yaml 或者 properties 文件配置参数来临时开启循环依赖(开启方式就是上面那段代码)。

三. 🦁Spring 容器解决循环依赖的原理是什么?

前面我们说了 Spring 容器可以处理部分循环依赖问题,只不过是高版本的 Spring 需要手动开启。那么 Spring 是如何解决这个问题的呢?

其实 Spring 是依靠三级缓存的方式来处理循环依赖的

那么它是如何检测存在循环依赖的呢?

其实也比较简单,就是容器在创建 实例A 的过程中,会给它贴一个正在创建的标签,说明它正在创建了,然后递归去创建其依赖的 实例B,当 实例B 也依赖 A 时,并且发现其正处于创建中的状态,那么就说明存在循环依赖了。

那么三级缓存是如何来解决它呢?

首先,我们可以认为实例化一个对象可以简单分为两步:为这个对象填充所需要的属性,而填充对象的属性的方式又有两种:
Spring 容器解决循环依赖可以理解为是在一个闭环中,先将第一个实例A实例化并提前暴露出来,这样闭环上依赖A的B就可以创建完成,那么依此类推,依赖 B 的 C 也可以创建出来了,从而递归可以顺利进行下去,当跳出最后一层递归后,A依赖的D也创建出来了,再将D注入到A上,整个闭环的实例就完成创建了。

  • 实例化该对象

  • 为这个对象填充所需要的属性,而填充对象的属性的方式又有两种:

    • 有参构造方法直接在对象创建时填充进去(Spring 无法自动处理这种方式创建实例的循环依赖);
    • 通过 set() 方法填充对象属性,而三级缓存仅对这种方法有效。

Spring 容器解决循环依赖可以理解为是在一个闭环中,先将第一个实例A实例化并提前暴露出来,这样闭环上依赖A的B就可以创建完成,那么依此类推,依赖 B 的 C 也可以创建出来了,从而递归可以顺利进行下去,当跳出最后一层递归后,A依赖的D也创建出来了,再将D注入到A上,整个闭环的实例就完成创建了

那么为什么说只有对于 set() 方法填充对象属性的方式,Spring容器才能解决呢?原因很简单(以B依赖A,A依赖B为例),A类 通过有参构造的方式在创建实例并同时将属性注入,那么在这个过程中(注意:此时A并没有完成实例创建),它会去寻找它依赖的对象B,此时B类也开始创建实例,但是由于A依赖B,并且A类并没有完成实例创建,所以二者就处在这种尴尬状态,导致最后容器报错!

然而,若是通过 set() 方法填充对象属性的方式,那么此时实例A已经创建完成,只是还没有注入对应的属性(这个我们后文暂且叫装配Bean吧,因为所谓的实例本质上就是一个Bean对象)而已。这就是为什么 Spring 容器为什么只能解决 set() 方法装配 Bean对象的循环依赖。

五. 🦁 三级缓存解决循环依赖的原理

我们前面提了好多次三级缓存!那么三级缓存到底是什么?它是如何解决循环依赖的?

Spring 容器的三个缓存分别如下:

// 1级缓存:存放实例化+属性注入+初始化+代理(如果有代理)后的单例beanprivate final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);// 2级缓存:存放实例化+代理(如果有代理)后的单例beanprivate final Map<String, Object> earlySingletonObjects = new HashMap<>(16);// 3级缓存:存放封装了单例bean(实例化的)的对象工厂private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

我们都知道 Spring 容器通过 IOC 创建单例对象,这个对象创建完成后最终都是存放在 singletonObjects 一级缓存里面的,但并不是所有的实例一开始创建就存进一级缓存!创建的时候需要放进不同的缓存。(具体的不讨论,我们现在来看一下循环依赖过程中,三级缓存是如何工作的!)

以 实例A 与 实例B 相互依赖为例子:

首先 Spring 容器先创建 A实例,实例化完成后将其封装为 ObjectFactory 对象先存入到三级缓存 singletonFactories 中;然后容器对 A 做进一步的装配,装配的时候发现 A 依赖 B ,所以 Spring 去三级缓存中寻找 B,发现其还没有创建,所以会先创建 B实例,B 创建完成后被封装为 ObjectFactory 对象被存入三级缓存 singletonObjects ,同时也会先进行装配,其间 Spring 也发现了 B 依赖于 A ,所以会回到三级缓存中寻找 A实例,终于在第三级缓存中发现了被封装为 ObjectFactory 对象的 A,将其取出来通过 getObject() 方法得到 A,拿到 A 后不再将其放回三级缓存,而是存进二级缓存 earlySingletonObjects 中,而三级缓存中的 ObjectFactoryA 也会被移除,这个过程相当于是 A 从三级缓存——>二级缓存。同时也将 A 填充给 B,至此B 完成装配,从三级缓存——>二级缓存,Spring 容器不会忘记还在"嗷嗷待哺"的 实例A,回过头去一级缓存找到 B将其填充给 A,至此 A 完成装配,从二级缓存——>一级缓存,创建结束,循环依赖完美解决。

六. 🦁 由有参构造方法注入属性的循环依赖如何解决?

我们通过一个案例来说明:实现两个相互依赖的类 A B:


@Component
public class A{private B b;public A(B b) {this.b = b;}
}@Component
public class B {private A a;public B(A a) {this.a = a;}
}

启动容器就会发现如下报错:

The dependencies of some of the beans in the application context form a cycle:┌─────┐
|  b defined in file [.../target/classes/com/demo/service/B.class]
↑     ↓
|  a defined in file [.../target/classes/com/demo/service/A.class]
└─────┘

我们通过在 A/B 类上的构造函数添加 @Lazy 注解则会解决这个循环依赖问题。如下:

@Component
public class A{private B b;public A(@Lazy B b) {this.b = b;}
}

此时则会正常启动了!!!

那么这其中的原理是什么呢?请看下回分解!

七.🦁 ENDing

错过的题目,自己要学会成长,下次再也不错啦!


在这里插入图片描述

🦁 其它优质专栏推荐 🦁

🌟《Java核心系列(修炼内功,无上心法)》: 主要是JDK源码的核心讲解,几乎每篇文章都过万字,让你详细掌握每一个知识点!

🌟 《springBoot 源码剥析核心系列》:一些场景的Springboot源码剥析以及常用Springboot相关知识点解读

欢迎加入狮子的社区:『Lion-编程进阶之路』,日常收录优质好文

更多文章可持续关注上方🦁的博客,2023咱们顶峰相见!

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

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

相关文章

vue3通过el-cascader实现动态菜单切换页面

如果只有一级菜单只会显示一个按钮 <div style"width: 100%; margin-top: 10px; display: flex; align-items: center; border-bottom: 1px solid #ccc;"><template v-for"(menu, index) in cascaderData" :key"index"><el-casc…

整数和浮点数在内存中存储

整数在内存中的存储 整数的2进制表⽰⽅法有三种&#xff0c;即原码、反码和补码。 对于整形来说&#xff0c;数据存放内存中的其实是补码。 在计算机系统中&#xff0c;数值一律用补码来表示和存储。原因是&#xff0c;使用补码&#xff0c;可以使符号位和数值域统一处理&am…

DUSt3R:简化三维重建

3D 重建是从二维 (2D) 图像创建对象或场景的 3D 虚拟表示的任务&#xff0c;可用于模拟、可视化或本地化等多种目的。 它广泛应用于计算机视觉、机器人和虚拟现实&#xff08;VR&#xff09;等多个领域。 在基本设置中&#xff0c;3D 重建方法输入一对图像 I1 和 I2&#xff0c…

关于Java对接网络验证+实践小例子,简单易懂

一个简单的网络验证小例子&#xff0c;各位大佬勿喷 突发奇想&#xff0c;如果一位A友找你拿一份 Working Fruits&#xff0c;但是你不想这位A友把你辛苦劳作、熬夜加点写出的代码分享他或她的另外一位朋友B友&#xff0c;也许并不是很有价值的一个小作业而已&#xff0c;但是就…

数据结构:详解【栈和队列】的实现

目录 1. 栈1.1 栈的概念及结构1.2 栈的实现1.3 栈的功能1.4 栈的功能的实现1.5 完整代码 2. 队列2.1 队列的概念及结构2.2 队列的实现2.3 队列的功能2.4 队列的功能的实现2.5 完整代码 1. 栈 1.1 栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的…

模拟B\S服务器(扩展知识点)

3.2 模拟B\S服务器(扩展知识点) 模拟网站服务器&#xff0c;使用浏览器访问自己编写的服务端程序&#xff0c;查看网页效果。 案例分析 准备页面数据&#xff0c;web文件夹。 复制到我们Module中&#xff0c;比如复制到day08中 我们模拟服务器端&#xff0c;ServerSocket类…

SpringCloud Alibaba实战和源码(8)OpenFeign使用

1、 使用Feign实现远程HTTP调用 1.1、常见HTTP客户端 HttpClient HttpClient 是 Apache Jakarta Common 下的子项目&#xff0c;用来提供高效的、最新的、功能丰富的支持 Http 协 议的客户端编程工具包&#xff0c;并且它支持 HTTP 协议最新版本和建议。HttpClient 相比传统 J…

RN开发搬砖经验之—处理“Duplicate class com.github.barteksc.pdfviewer“

问题信息 Duplicate class com.github.barteksc.pdfviewer.PDFView found in modules jetified-AndroidPdfViewer-3.1.0-beta.3-runtime (com.github.TalbotGooday:AndroidPdfViewer:3.1.0-beta.3) and jetified-android-pdf-viewer-2.8.2-runtime (com.github.barteksc:andro…

为车主提供多路况安全保障!“北欧轮胎安全专家”熊牌轮胎迎来全新升级

德国马牌轮胎旗下明星品牌——Gislaved熊牌轮胎迎来全新升级。 自进入中国市场以来&#xff0c;熊牌轮胎凭借着坚韧安全、静音降噪等特点&#xff0c;收获无数好评。此次全新升级的熊牌轮胎&#xff0c;在品牌logo中加入了“北欧棕熊”的形象&#xff0c;并且对此前轮胎标签中的…

qt使用Windows经典风格,以使QTreeView或QTreeWidge有节点线或加号

没有使用Windows经典风格的QTreeView或QTreeWidget显示如下&#xff1a; 使用Windows经典风格的QTreeView或QTreeWidget显示如下&#xff1a; 树展开时&#xff1a; 树未展开时&#xff1a; 可以看到&#xff1a; 未使用Windows经典风格时&#xff0c;QTreeView或QTreeWidget…

【MySQL】基本查询(1)

【MySQL】基本查询&#xff08;1&#xff09; 目录 【MySQL】基本查询&#xff08;1&#xff09;表的增删改查Create单行数据 全列插入多行数据 指定列插入插入否则更新替换 RetrieveSELECT 列全列查询指定列查询查询字段为表达式为查询结果指定别名结果去重 WHERE 条件英语不…

第六篇:视频广告格式上传指南(上) - IAB视频广告标准《数字视频和有线电视广告格式指南》

第六篇&#xff1a; 视频广告格式和上传指南&#xff08;上&#xff09; --- 我为什么要翻译介绍美国人工智能科技公司IAB系列技术标准&#xff08;2&#xff09; 流媒体数字视频的广告格式分为线性和非线性两大类。任何一个广告都可以与显示在视频播放器外部的伴随横幅一起提…

【Linux文件系列】重定向

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

CMake学习(下)

1. 嵌套的CMake 如果项目很大&#xff0c;或者项目中有很多的源码目录&#xff0c;在通过CMake管理项目的时候如果只使用一个CMakeLists.txt&#xff0c;那么这个文件相对会比较复杂&#xff0c;有一种化繁为简的方式就是给每个源码目录都添加一个CMakeLists.txt文件&#xff…

windows系统下python进程管理系统

两年来&#xff0c;我们项目的爬虫代码大部分都是放在公司的windows机器上运行的&#xff0c;原因是服务器太贵&#xff0c;没有那么多资源&#xff0c;而windows主机却有很多用不上。为了合理利用公司资源&#xff0c;降低数据采集成本&#xff0c;我在所以任务机器上使用anac…

将本地的项目上传到gitee,

场景&#xff1a;在本地有一个项目&#xff0c;想要把这个项目上传到gitee&#xff0c;且在gitee中已经创建好仓库 依次执行下图中的命令&#xff1a;

【linux】进程地址空间(进程三)

目录 快速了解&#xff1a;引入最基本的理解&#xff1a;细节&#xff1a;如何理解地址空间&#xff1a;a.什么是划分区域&#xff1a;b.地址空间的理解&#xff1a; 为什么要有进程空间&#xff1f;进一步理解页表与写时拷贝&#xff1a; 快速了解&#xff1a; 先来看这样一段…

2024年起重机司机(限桥式起重机)证考试题库及起重机司机(限桥式起重机)试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年起重机司机(限桥式起重机)证考试题库及起重机司机(限桥式起重机)试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作…

第四范式2023全年业绩:营收人民币42.0亿元同比增长36.4%,行业大模型为千行万业赋能...

3月20日&#xff0c;第四范式&#xff08;06682.HK&#xff09;公布2023年全年业绩&#xff0c;营收稳步增长&#xff0c;盈利节奏清晰。 第四范式定位人工智能时代的软件企业&#xff0c;致力于用人工智能技术赋能千行万业&#xff0c;帮助各行业发现更多规律&#xff0c;形成…

【排序】插入排序与选择排序详解

文章目录 &#x1f4dd;选择排序是什么&#xff1f;&#x1f320;选择排序思路&#x1f309; 直接选择排序&#x1f320;选择排序优化&#x1f320;优化方法&#x1f309;排序优化后问题 &#x1f320;选择排序效率特性 &#x1f309;插入排序&#x1f320;插入排序实现 &#…