首页 > 其他分享 >三探循环依赖 → 记一次线上偶现的循环依赖问题

三探循环依赖 → 记一次线上偶现的循环依赖问题

时间:2022-11-03 12:37:18浏览次数:86  
标签:依赖 实例 Spring 扫描 偶现 Bean 循环 MySender BeanDefinition

开心一刻

  心里一直在想明天该以何种方式祭拜列祖列宗,彻夜难眠,辗转反侧,最好下定了决心

  给弟发了个微信:别熬夜了,早上早点起来,咱俩去上坟

  弟:知道了,哥

  我:记得带上口罩

  弟:坟就在家后边的山上,这么近带什么口罩?

  我:就你这逼样,好意思见列祖列宗?

  弟:我知道了,那哥你带吗?

  我:我也带

三探循环依赖 → 记一次线上偶现的循环依赖问题_实例化

前情回顾

  一探

  ​​Spring 的循环依赖,源码详细分析 → 真的非要三级缓存吗​​ 中讲到了循环依赖问题

Spring 只能解决 setter

Spring 是如何解决 setter

  二探

Spring

  所以进行了二探:​​再探循环依赖 → Spring 是如何判定原型循环依赖和构造方法循环依赖的​​?

Spring

  感兴趣的可以去看下

 

  大家跟源码的时候,一定要注意版本!!!

项目模拟

Spring

Spring

SpringBoot 版本是 2.0.3.RELEASE

k8s 部署,本地环境未采用 k8s

pod

  问题偶发,而非必现,很是头疼,但问题还是得解决,从提示信息着手呗

  根据错误提示信息,楼主模拟出了一个简化的工程,方便我们进行问题排查

三探循环依赖 → 记一次线上偶现的循环依赖问题_构造方法_02

 

  非常简单,完整地址:​​spring-other-circular-reference​

  我们来看下类图

三探循环依赖 → 记一次线上偶现的循环依赖问题_Spring 循环依赖_03

MyListener 、 MyService 、 MyManager 很常规,特殊的是 MyConfig 和 MySender

三探循环依赖 → 记一次线上偶现的循环依赖问题_构造方法_04

三探循环依赖 → 记一次线上偶现的循环依赖问题_spring_05

问题复现

  如果按上述工程结构,本地很难复现问题 ,反正楼主是没复现出来

MySender

三探循环依赖 → 记一次线上偶现的循环依赖问题_Spring 循环依赖_06

  启动失败,错误信息如下:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myConfig': Unsatisfied dependency expressed through field 'myListener'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myListener': Unsatisfied dependency expressed through field 'myService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myServiceImpl': Unsatisfied dependency expressed through field 'myManager'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myManager': Unsatisfied dependency expressed through field 'mySender'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'mySender': Requested bean is currently in creation: Is there an unresolvable circular reference?

Is there an unresolvable circular reference?

问题分析

  我们从以下几个方面来分析

  BeanDefinition 扫描

XML 方式的 Bean 定义越来越少,除了一些遗留的老项目,基本看不到 XML 方式的 Bean

Bean

  文件夹的扫描顺序与文件夹名字的升序一致,文件的顺序与文件名的升序一致,如下所示

三探循环依赖 → 记一次线上偶现的循环依赖问题_Spring 循环依赖_07

ConfigurationClassParser 类中 doProcessConfigurationClass

三探循环依赖 → 记一次线上偶现的循环依赖问题_构造方法_08

@ComponentScan 的处理早于 @Bean

BeanDefinition 扫描过程中,会按扫描顺序会往 DefaultListableBeanFactory 的 beanDefinitionMap 中添加 BeanDefinition ,往 beanDefinitionNames 添加 BeanName

  我们来跟下源码,看是不是如上所说

三探循环依赖 → 记一次线上偶现的循环依赖问题_实例化_09

BeanDefinition 的 BeanName 会被先添加到 beanDefinitionNames

  BeanDefinition 覆盖

MyConfig 中通过 @Bean 定义了 MySender ,而 MySender 类上又用了 @Component

MySender

  ​​关于 Spring Boot 中创建对象的疑虑 → @Bean 与 @Component 同时作用同一个类,会怎么样?​​从源码的角度分析了这个问题

SpringBoot 2.0.3.RELEASE 中, @Configuration + @Bean 修饰的 BeanDefinition 会覆盖掉 @Component 修饰的 BeanDefinition

MySender 类上的 @Component 其实没用,加不加效果是一样的,这里说的 没用效果 仅仅指的是 MySender 的 BeanDefinition

  Bean 实例化顺序

BeanDefinition 用来构建实例,那么 MySender 上的 @Component 就有作用了,它决定了 MySender

MyConfig 、 MyListener 、 MyServiceImpl 、 MyManager

Bean

三探循环依赖 → 记一次线上偶现的循环依赖问题_spring_10

Bean 会先被实例化; Bean 实例化的过程中会填充属性,可能会导致后被扫描的 Bean

Bean 之间没有依赖,那么会严格按照 Bean

  再看问题

  我们再回到前面的问题

三探循环依赖 → 记一次线上偶现的循环依赖问题_实例化_11

Is there an unresolvable circular reference?

MyConfig 、 MyListener 、 MyManager 、 MyServiceImpl , MySender

MyConfig 中通过 @Bean 修饰了 MySender 的 BeanDefinition

三探循环依赖 → 记一次线上偶现的循环依赖问题_spring_12

MySender 自身的无参 BeanDefinition

MySender 的有参构造方法来创建 MySender

myListener ,所以去 Spring 容器中找 MyListener 实例,没有找到则创建,然后填充 MyListener

  以此类推,实例的创建过程如下所示:

三探循环依赖 → 记一次线上偶现的循环依赖问题_实例化_13

Is there an unresolvable circular reference?

  相当于是变种的构造方法循环依赖

  最初状态

MySender

三探循环依赖 → 记一次线上偶现的循环依赖问题_构造方法_14

MyConfig

三探循环依赖 → 记一次线上偶现的循环依赖问题_Spring 循环依赖_15

  对象是都可以正常实例化、初始化的

Is there an unresolvable circular reference?

  线上问题

Is there an unresolvable circular reference?

k8s 部署过程中, BeanDefinition

问题修复

  虽然我们没能找到线上问题的确切原因,但还是有办法去根治这个问题的

Spring

MyConfig , MySender

三探循环依赖 → 记一次线上偶现的循环依赖问题_构造方法_16

 

MySender

三探循环依赖 → 记一次线上偶现的循环依赖问题_spring_17

 

@PostConstruct

 总结

BeanDefinition

    如果我们去跟源代码就会发现,以启动类为起点,扫描启动类同级目录下的所有文件夹 

    按文件夹名升序顺序进行扫描,会递归扫描每个文件夹

    文件扫描也是按文件名升序顺序进行

Spring 会偶发的随机扫描,还是 pod

BeanDefinition

Spring

BeanDefinition 覆盖并不会影响 BeanDefinition

BeanName 在 beanDefinitionNames 中的位置,即不会影响 Bean

Bean

    理论上来讲,先被扫描到的就先被实例化,但实例化过程中的属性填充会打乱这个顺序,会将被依赖的对象提前实例化

Spring

    一定要结合版本来看问题

    版本不同,底层实现可能会不同



标签:依赖,实例,Spring,扫描,偶现,Bean,循环,MySender,BeanDefinition
From: https://blog.51cto.com/u_13423706/5819380

相关文章

  • 四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!
    开心一刻那天知道她结婚了,我整整一个晚上没睡觉,开了三百公里的车来到她家楼下,缓缓的抽了一支烟......天渐渐凉了,响起了鞭炮声,迎亲车队到了,那天披着婚纱的她很美,真的......
  • 还在用双层for循环吗?太慢了
    前情提要我们在开发中经常碰到这样的场景,查出两个list集合数据,需要根据他们相同的某个属性为连接点,进行聚合。但是平时我们使用的时候关注过性能吗?下面让我们一起来看看......
  • 学习vue3(五)(插槽slot,Teleport传送组件,keep-alive缓存组件,依赖注入Provide / Inject)
    插槽slot插槽就是子组件中的提供给父组件使用的一个占位符,用<slot></slot>表示,父组件可以在这个占位符中填充任何模板代码,如HTML、组件等,填充的内容会替换子组件的<slot......
  • 控制循环
    目录控制循环选择循环的种类循环的灵活度和检查位置什么时候使用while循环什么时候用带退出的循环循环控制前言进入循环控制循环使用循环是编程中最复杂的方面之一:知道......
  • vue导入项目缺少依赖‘node_modules’
    从git下载好的项目,导入vue时提示‘node_modules’依赖则需要在你的项目包下面找是否有package-lock.json文件,如:如果有,但是依旧报错,直接删除package-lock.json文件,用终......
  • 循环~倒数字三角形
    题目描述数字三角形的特征是三角形由数字填满,每行数字从1到9再到0周而复始挨个排列。你的任务是根据整数值输出倒数字三角形。输入输入数据含有一些整数,其值在[1,40]之......
  • 循环~画高楼
    题目描述小明想拥有一栋有烟囱的房子。 请写程序从键盘输入n(限定n为小于10的正整数),然后参照样例,用"*"画出一栋n层的楼房。 不管有几层,楼房的屋顶和烟囱都是一样的。 ......
  • 依赖注入_非空断言,css使用v-bind
    provide依赖注入//provide和inject通常成对一起使用,使一个祖先组件作为其后代组件的依赖注入方,无论这个组件的层级有多深都可以注入成功,只要他们处于同一条组件链上。......
  • 求和及求平均-while循环语句的应用-2022-11-2
    packagescanner;importjava.util.Scanner;publicclassDemo04{publicstaticvoidmain(String[]args){Scannerscanner=newScanner(System.in);......
  • Linux 循环,格式以及使用方法
    ​for循环:(每读取一行在字符串下面添加#号)[root@localhost]#cat/etc/passwd|awk-F':''{print$3}'|tail-5>test1.txt[root@localhost]#cattest1.txt98972......