首先看下spring创建一个bean的简单流程,假如beanA引用beanB,beanB引用beanA,spring在初始化beanA的时候会造成循环依赖(这里讲的是单例,spring底层只对单例循环依赖进行解决)。

在记录之前我写了两个测试类进还原循环依赖,一个是ClassA,里面的引用了ClassB,同时ClassB也引用了ClassA。这样子ClassA和ClassB形成了循环依赖。

spring容器底层在创建ClassB Bean会调用getSingleton先去从一级缓存singletonObjects中拿,如果一级缓存没有,则去二级缓存earlySingletonObjects中拿,二级缓存中没有,则去三级缓存singletonFactories中拿,如果都没有,则调用createBean方法开始创建这个ClassB这个Bean。

在调用 createbean之前spring会调用isPrototypeCurrentlyInCreation方法来判断当前这个bean存在正在创建中的缓存中(prototypesCurrentlyInCreation),如果不是则放入该缓存中,用于后面循环依赖的解决。因为ClassB是第一次被spring容器加载,所以肯定是空,这时候被放入正在创建中的缓存中。

接着createbean会调用doCreateBean方法,这个真正执行创建bean的方法。该方法调用createBeanInstance(beanName, mbd, args)通过后置处理器判断调用ClassB的构造方法并创建返回ClassB的实例对象,此时对象的ClassA的引用肯定是空,因为ClassB的默认构造方法,并没有对ClassA赋值。接着spring会将这个早期对象放入三级缓存singletonFactories中。

放入三级缓存singletonFactories结束后,调用populateBean方法进行属性赋值。populateBean中会去判断ClassB这个Bean有哪些属性以及属性的类型并选择调用哪个方法来进行赋值,如果是引用类型则调用resolveReference方法进行属性赋值。

进入resolveReference方法,spring会先去判断当前容器是否有父容器,如果有则从父容器中获取引用对象ClassA,如果没有则从当前容器中获取引用对象ClassA。(spring允许子容器使用父容器的bean,就是在这里体现出来,比如springmvc)这时候程序会调用this.beanFactory.getBean(resolvedName)。从容器中再次获取ClassA这个引用对象。此时getBean会调用doGetBean从新走刚才创建ClassB对象流程。

同样spring在创建ClassB的引用对象ClassA时,也会去解析ClassA的引用对象。此时ClassA的引用对象是ClassB(此时ClassB对象是暴露在三级缓存中的),这是时候通用调用当前容器的getBean--->doGetBean,在doGetBean中调用getSingleton(beanName)方法。在这里我们会看到spring在从二级或三级缓存中获取对象是有条件的,条件即使这个对象正在创建中。通过isSingletonCurrentlyInCreation(beanName)这个方法去判断的。

确实刚创建ClassB的时候记录了ClassB正在创建中,这时候从一级缓存中获取(肯定没有因为ClassB还没完全实例化完),接着继续从二级缓存中获取也没有(因从刚才分析下来,ClassB只单单暴露在三级缓存中),这时候调用singletonObject = singletonFactory.getObject()这个方法,从三级缓存中拿到了早期对象。singletonFactory.getObject()会进一步调用getObject()方法中的getEarlyBeanReference,这里面调用了spring的后置处理器,给开发者对早期对象能够进行提前的修改。比如ClassB 对象里有String str="";可以提前对str进行操作。所以为什么spring有二级缓存就可以解决循环依赖。还要用到三级缓存。spring在三级缓存给开发提供了给早期对象扩展的功能。此时将扩展完的早期对象ClassB放入二级缓存,移除三级缓存,并将这个对象返回出去。

此时ClassA对象ClassB的引用已经拿到值了,所以创建一直往下走,走到addSingleton(beanName, singletonObject)这个方法,将ClassA放入一级缓存singletonObjects,从二级缓存和三级缓存中移除。因为ClassA的bean已经创建完成了,二级缓存和三级缓存已经没用了。此时完整的ClassA的bean返回出去,ClassB的属性ClassA也得到了赋值,ClassB的bean可以继续创建了。

注意:spring在通过构造器给属性赋值是无法解决循环依赖的,从上面分析来看spring执行createBeanInstance方法去判断调用当前bean的构造方法,此时还未放入三级缓存中。如果是bean作用域是原型也是无法解决循环依赖,因为原型对象并没有放在缓存中。

spring处理循环依赖时序图_spring--解决循环依赖相关推荐

  1. gradle下载更新依赖库失败的解决办法 - 依赖库下载加速1000%

    gradle下载更新依赖库失败的解决办法 - 依赖库下载加速1000% 现在使用gradle来构建项目的越来越多,通过gradle能更轻松完成项目的依赖以及编译等工作.特别是Google的Androi ...

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

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

  3. 什么是Maven依赖冲突,如何解决Maven依赖冲突

    在解决冲突之前首先我们要知道什么是依赖冲突,这样可以更好的去锁定bug原因.下面通过一个代码示例来真正展示出依赖冲突. 目录 依赖冲突代码演示 1.添加依赖 2.观察依赖的层次结构 3.运行项目jra ...

  4. Maven依赖原则及如何解决Maven依赖冲突

    前言 在大数据应用中,现在发现依赖关系非常复杂,在上线之前很长测试,前一段时间在部署udf 出现了导致生产Hiveserver2 宕机问题,出现严重事故.现在就咨询研究一下.Maven虽然已经诞生多年 ...

  5. spring处理循环依赖时序图_Maven依赖管理系统

    [思考] 首先,简单讲述一下为什么需要这样一个系统? 不知道大家有没有思考,在一个可能有上千个模块/产品的公司,对于模块之间有较多相互依赖的情况,以下问题该如何解决: 我们把一个生命周期结束的组件移除 ...

  6. idea导入maven项目依赖报错_解决Maven依赖冲突的好帮手,这款IDEA插件了解一下?

    1.何为依赖冲突 Maven是个很好用的依赖管理工具,但是再好的东西也不是完美的.Maven的依赖机制会导致Jar包的冲突. 举个例子,现在你的项目中,使用了两个Jar包,分别是A和B.现在A需要依赖 ...

  7. android 无限循环 简书,Android Handler 解决循环引用

    案例 public class MainActivity extends AppCompatActivity { private Handler mHandler = new Handler(){ @ ...

  8. UML图详解(七)——交互图(时序图与协作图)

    一.概念 交互图描述对象之间的动态合作关系以及合作过程中的行为次序. 交互图常常用来描述一个用例的行为,显示该用例中所涉及的对象以及这些对象之间的消息传递情况,即一个用例的实现过程. 交互图有顺序图和 ...

  9. 京东一面:Spring 为何需要三级缓存解决循环依赖,而不是二级缓存?我懵了。。...

    欢迎关注方志朋的博客,回复"666"获面试宝典 来源:cnblogs.com/semi-sub/p/13548479.html 前言 bean生命周期 三级缓存解决循环依赖 总结 ...

  10. 为什么Spring需要三级缓存解决循环依赖,而不是二级缓存?

    来源:https://www.cnblogs.com/semi-sub/p/13548479.html 在使用spring框架的日常开发中,bean之间的循环依赖太频繁了,spring已经帮我们去解决 ...

最新文章

  1. 关系数据理论中的范式
  2. java使用HttpClient传输json格式的参数
  3. angular之Rxjs异步数据流编程入门
  4. 20161011L04-03老男孩linux运维实战培训-Linux系统的用户和用户组管理-01
  5. python numpy.mean() axis参数使用方法【sum(axis=*)是求和,mean(axis=*)是求平均值】
  6. VueRouter源码详细解读
  7. C#设计模式(9)——装饰者模式(Decorator Pattern)
  8. ajax实现上传文件
  9. 属于程序员的黄金五年,把握这 5 年,或将迎来美好的职场生活!
  10. 复联4里用到的方法论
  11. C51单片机————中断系统
  12. 解封装(八):av_seek_frame改变播放进度函数
  13. 如何设置STM8单片机选项字
  14. 用php做一个网站,教你如何用PHP开发一个完整网站
  15. Kotlin基础篇(二)-作用域函数
  16. python实现ddos防护_python实现的防DDoS脚本
  17. 【信息系统项目管理师】干系人管理
  18. ios下 KeyChain 的研究
  19. 1、模拟蚂蚁借呗—利息计算
  20. 计算机一级考试各种快捷键,2021年计算机等级考试重点讲解:(一级PS)文件操作常用快捷键...

热门文章

  1. 微服务中的健康监测以及其在ASP.NET Core服务中实现运行状况检查
  2. c/c++ 多线程 ubuntu18.04 boost编译与运行的坑
  3. Codeforces Round #499 (Div. 2) C. Fly(数学+思维模拟)
  4. 基础 - jQuery
  5. Swift - 通过url地址打开web页面
  6. Single Number leetcode java
  7. JS使用setInterval定时器导致前端页面卡死
  8. 使用pythonping检查设备的连通性并记录可达设备(eNSP模拟器)
  9. ppp协议、chap认证、HDLC封装、MGRE、GRE综合实验
  10. NJUPT_CTF easychallenge 解题脚本