Spring循环依赖的解决方法详解

 更新时间:2022年08月16日 08:55:19   作者:飘飘~  
Spring的解决循环依赖是有前置条件的,要解决循环依赖我们首先要了解Spring Bean对象的创建过程和依赖注入的方式。依赖注入方式,我之前的博客有所分享,大家可以在看本篇文章之前进行一下小小的回顾

说明:spring如何解决循环依赖,是面试中经常问到的题目,今天我们就来分享一下spring是如何解决循环依赖问题的。

什么是循环依赖:

我们先来看看官方文档的说法:

通俗来讲,就是A依赖B或者B依赖A,或者C依赖自己本身,或是三个以上,例如A依赖B,B依赖C,C又依赖A。如下图:

Spring实例Bean的本质

Spring在实例化一个bean的时候,是首先递归的实例化其所依赖的所有bean,直到某个bean没有依赖其他bean,此时就会将该实例返回,然后反递归的将获取到的bean设置为各个上层bean的属性的。

循环依赖主要场景

什么情况下循环依赖可以被解决

Spring解决循环依赖是有前置条件的

  • 出现循环依赖的Bean必须要是单例(singleton),如果依赖prototype则完全不会有此需求。
  • 依赖注入的方式不能全是构造器注入的方式。

解决方式

Spring是通过三级缓存来解决上述问题的:

一级缓存: singletonObjects存储的是所有创建好了的单例Bean

二级缓存:earlySingletonObjects完成实例化,但是还未进行属性注入及初始化的对象

三级缓存:singletonFactories提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象。

三级缓存解决循环依赖流程:

  1. 获取A时首先会尝试从一级缓存singletonObjects中获取;
  2. 获取不到就再从二级缓存earlySingletonObjects中获取;
  3. 若是还没有则尝试从三级缓存singletonFactories获取;
  4. 还是没有获取到则再尝试创建A对象
  5. 会执行doGetBean->createBean->createBeanInstance并使用构造器实例化
  6. 在尝试给A进行初始化时,由于B不存在无法完成初始化,则将半成品A放入第二级缓存中,进入B的创建流程。
  7. 与先前过程相似,在第三级缓存中放入beanName和表达式sharedInstance,进入B的初始化过程
  8. 由于在第二级缓存中可以找到A,则B可以完成初始化,将成品Bean放入一级缓存中备用,删除三级缓存中的B
  9. 同时完成A的初始化,并删除二级缓存中的半成品A

具体流程图如下:

最后我们来个小小的总结:

Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池(singletonObjects),二级缓存为早期曝光对象earlySingletonObjects,三级缓存为早期曝光对象工厂(singletonFactories)。

当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。

当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取。

  • 先获取到三级缓存中的工厂;
  • 调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。
  • 当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!

到此这篇关于Spring循环依赖的解决方法详解的文章就介绍到这了,更多相关Spring循环依赖内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java队列中Queue与Deque的区别面试精讲

    java队列中Queue与Deque的区别面试精讲

    这篇文章主要为大家介绍了java队列中Queue与Deque的区别面试精讲,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • 解决使用@Value(${×××))从properties文件取值的坑

    解决使用@Value(${×××))从properties文件取值的坑

    这篇文章主要介绍了解决使用@Value(${×××))从properties文件取值的坑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Java在PowerPoint幻灯片中创建散点图的方法

    Java在PowerPoint幻灯片中创建散点图的方法

    散点图是通过两组数据构成多个坐标点,考察坐标点的分布,判断两变量之间是否存在某种关联或总结坐标点的分布模式,这篇文章主要介绍了Java如何在PowerPoint幻灯片中创建散点图,需要的朋友可以参考下
    2023-04-04
  • Java实现为Word每一页设置不同图片水印的效果

    Java实现为Word每一页设置不同图片水印的效果

    Word中设置水印时,可加载图片设置为水印效果,但通常添加水印效果时,会对所有页面都设置成统一效果。所以本文为大家介绍了一个方法,可以实现对每一页或者某个页面设置不同的水印效果,需要的可以参考一下
    2022-02-02
  • Spring配置文件如何使用${username}

    Spring配置文件如何使用${username}

    这篇文章主要介绍了Spring配置文件如何使用${username},文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • Java 详解分析链表的中间节点

    Java 详解分析链表的中间节点

    链表是基本的数据结构之一,面试题中链表占很大一部分,可见链表操作是非常重要的。本篇文章我们来探究一下如何获取链表的中间节点
    2022-01-01
  • mybatis升级mybatis-plus时踩到的一些坑

    mybatis升级mybatis-plus时踩到的一些坑

    这篇文章主要给大家介绍了关于mybatis升级mybatis-plus时踩到的一些坑,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • GateWay动态路由与负载均衡详细介绍

    GateWay动态路由与负载均衡详细介绍

    这篇文章主要介绍了GateWay动态路由与负载均衡,GateWay支持自动从注册中心中获取服务列表并访问,即所谓的动态路由
    2022-11-11
  • Java使用正则表达式获取子文本的方法示例

    Java使用正则表达式获取子文本的方法示例

    这篇文章主要介绍了Java使用正则表达式获取子文本的方法,结合实例形式分析了java针对子文本的正则操作相关函数与使用技巧,需要的朋友可以参考下
    2017-09-09
  • 数组在java中的扩容的实例方法

    数组在java中的扩容的实例方法

    在本篇文章里小编给大家分享的是一篇关于数组在java中的扩容的实例方法内容,有兴趣的朋友们可以学习下。
    2021-01-01

最新评论