一、背景

清分服务添加一个异步处理功能(@asyc),本地测试时发现启动服务后有时正常有时异常。

二、代码再现

1、启动类代码

public  static  void  main(String[] args)  throws  Exception {
  String configLocation =  "classpath:spring/*.xml" ;
  @SuppressWarnings ( "resource" )
  ClassPathXmlApplicationContext context =  new  ClassPathXmlApplicationContext(configLocation);
  while  ( true ) {
     try  {
         Thread.sleep( 60  * 1000L);
     catch  (Exception e) {
     }
  }
}

2、服务启动后报错,如下:

三、问题分析

1、相同的环境,启动服务结果不同,定位为工程代码引起该异常。

2、启动类中,配置文件名采用了通配符,理论上配置文件加载顺序不固定。

3、从机器上的日志可以看出,启动成功和异常时spring/application-xxx.xml加载顺序不同。

4、从错误日志可以看出两个问题点:循环依赖和不同的bean依赖了同一个bean的不同版本。(spring默认支持循环依赖)

说明:

1、bean的实例化过程:create bean →  cache bean → inject element → inject element → inject element ... ... → finish(debug模式可以跟踪完整的bean实例化过程)

2、非循环依赖场景

条件:A依赖B,如果先create A

实例化过程为:create A → cache A → create B → cache B → finish B → inject B to A → finish A。(被依赖对象先finish,即B先于A实例化完成)

3、循环依赖场景

条件:A依赖B,B依赖A,如果先create A

实例化过程为:create A → cache A → create B → cache B → inject A to B → finish B → inject B to A → finish A。(spring允许eagerly cached instance注入到其他bean中,eagerly cached instance 指已缓存但未finish的bean)

清分工程中bean的依赖比较复杂,抽象出下面的场景做说明:(下面的bean都为单例模式,非单例模式下bean不会进行cache,不支持循环依赖)

步骤1:A依赖B、C依赖B,B依赖C,并且B实现类的方法上存在@async注解,即B在实例化完成前会生成代理。
步骤2:A开始create,发现B未实例化,于是先cache A。
步骤3:B开始create,发现C未实例化,于是先cache B。
步骤4:C开始create,发现B已实例化,取出cache的B,注入C中,C finish。
(为什么B未实例化完就注入到C中了?that is not fully initialized yet - a consequence of a circular reference,因为存在循环依赖,所以允许先注入。猜测未做是否被代理的检测)
步骤5:生成B的代理类B@108e。
步骤6:B@108e注入到A时,发现B@108e的原始实例B已注入到循环引用的C中,却没有使用B的最终代理类B@108e,所以抛异常。

正常情况应该为B先finish,A和C后finish,这样可以保证B的最终代理类注入到A和C中。

四、小心陷阱

1、depends-on设置:如果A depends-on B,只会保证B先于A开始实例化(Creating instance of bean),但不保证B先于A实例完成(Finished creating instance of bean)。对于简单的bean,实例化完成顺序正常。

2、尽量保持bean的依赖关系简单。

3、保证bean加载顺序固定。

spring循环依赖问题排查相关推荐

  1. 这个Spring循环依赖的坑,90%以上的人都不知道

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:Mythsman 原文:https://blog.myths ...

  2. 烂大街的Spring循环依赖该如何回答?

    什么是循环依赖? 从字面上来理解就是A依赖B的同时B也依赖了A,就像上面这样,或者C依赖与自己本身.体现到代码层次就是这个样子 @Component public class A {// A中注入了B ...

  3. 【spring容器启动】之bean的实例化和初始化(文末附:spring循环依赖原理)

    本次我们通过源码介绍ApplicationContext容器初始化流程,主要介绍容器内bean的实例化和初始化过程.ApplicationContext是Spring推出的先进Ioc容器,它继承了旧版 ...

  4. 终于有人把 Spring 循环依赖讲清楚了!

    网上关于Spring循环依赖的博客太多了,有很多都分析的很深入,写的很用心,甚至还画了时序图.流程图帮助读者理解,我看了后,感觉自己是懂了,但是闭上眼睛,总觉得还没有完全理解,总觉得还有一两个坎过不去 ...

  5. spring处理循环依赖时序图_spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖...

    本次博客的目标 1. 手写spring循环依赖的整个过程 2. spring怎么解决循环依赖 3. 为什么要二级缓存和三级缓存 4. spring有没有解决构造函数的循环依赖 5. spring有没有 ...

  6. Spring循环依赖源码剖析

    Spring循环依赖源码剖析 一.场景介绍 二.整理执行流程总结 三.源码分析 编写测试类 /*** 测试循环依赖*/@Testpublic void testCyclicDependence(){A ...

  7. Spring 循环依赖(circular dependency)

    一.什么是循环依赖 循环依赖即循环引用,形成闭环.比如,A 依赖 B,B 又依赖 A,形成了循环依赖:或者 A 依赖 B,B 依赖 C,C 又依赖 A,形成了循环依赖:更或者是自己依赖自己.如图: 这 ...

  8. spring循环依赖让你更好的理解spring!!

    什么是循环依赖 一言以蔽之:两者相互依赖. 在开发中,可能经常出现这种情况,只是我们平时并没有注意到原来我们写的两个类.甚至多个类相互依赖了,为什么注意不到呢?当然是因为没有报错,而且一点问题都木有, ...

  9. 帮助你更好的理解Spring循环依赖

    网上关于Spring循环依赖的博客太多了,有很多都分析的很深入,写的很用心,甚至还画了时序图.流程图帮助读者理解,我看了后,感觉自己是懂了,但是闭上眼睛,总觉得还没有完全理解,总觉得还有一两个坎过不去 ...

最新文章

  1. COMP 0137 Machine Vision
  2. 【Matlab 控制】仿真多智体一致性分析,附代码
  3. ABAP计算间隔月份
  4. 恕我直言,很多调参侠搞不清数据和模型谁更重要
  5. 应用squid全面加速web(全)
  6. 获2017中国最佳创业投资机构百强,西高投二次创业实现超越
  7. struct和class的区别
  8. [双调]落梅风(寿阳曲).焰火似龙
  9. 麦迪逊大学计算机科学咋样,威斯康星大学麦迪逊分校计算机专业排名
  10. Tableau画图初学者~新手教程~常见类型图
  11. 内部收益率计算公式用计算机,内部收益率的计算公式是什么
  12. UE4 | 学习Shader - “赏月、玩灯笼”
  13. java微信公众号素材管理系统_微信公众平台后台素材管理
  14. RKH81 键盘快捷键
  15. 使用canvas实现水印效果
  16. 图形学基础 | mtl文件详解
  17. 黑客水平测试 大家可以自测一下
  18. Java中有哪些锁,区别是什么
  19. Mac系统下无法删除文件夹
  20. 期货多空代码(期货多空趋势指标源码)

热门文章

  1. 私有云产业在各方推动下不断前进稳定发展,前景广阔
  2. 干货|面试结束后应该做的5件事
  3. 罗列一下“流氓软件”的基本行为
  4. Window 7 浅绿色 保护眼睛 电脑保护色
  5. ModuleNotFoundError: No module named '_pywrap_tensorflow_internal'--解决方法
  6. 模型构建器 空间位置选择_如何在2020年选择最佳网站构建器(比较)
  7. php 简单计算权重的方法(适合抽奖类的应用)
  8. 采用邻接表存储有向图,设计算法判断任意两个顶点间是否存在路径。设计算法,将一个无向图的邻接矩阵转换为邻接表。
  9. ens文件如何使用,什么意思
  10. 2023全国特种作业操作证高压电工试卷一[安考星]