在Spring中如何处理循环依赖问题

 更新时间:2025年01月07日 08:38:05   作者:MuseLss  
Spring解决循环依赖的关键在于提前曝光未完全创建的bean,在Spring中创建Bean分为实例化、属性填充和初始化三步,循环依赖的解决思路是先创建A的bean实例,此时A是不完整的,用一个Map保存不完整的A,再创建B,B需要注入A

Spring如何处理循环依赖

解决的关键就在于提前曝光未完全创建的bean。

在Spring中创建Bean分为三步

  • 实例化,createBeanInstance,即new一个bean对象。
  • 属性填充,populateBean,即往bean对象中set属性值。
  • 初始化,initializeBean。

循环依赖的解决思路

  • 先创建A的bean实例,此时的A是不完整的,因为没有属性填充(即B依赖没有注入),此时用一个Map保存不完整的A,
  • 再创建B,B需要注入A,所以可以从Map中得到不完整的A,此时B就完整了,然后A就可以注入B了。

在Spring中,只有同时满足以下两点才能解决循环依赖的问题。

1.依赖的bean必须都是单例。

  • 因为如果是原型模式的话是需要创建一个新的对象,创建A1的时候,需要创建A1的依赖B1
  • 那么在创建B1的时候,又需要创建B1的依赖A2,这样就要创建B2,A3,B3……,进入无限的创建对象的过程

2.依赖注入的方式,不能全是构造函数注入。

  • 如果全是构造函数注入,即A(B b) ,那么表明在创建A的Bean的实例的时候,就需要得到B,那么此时就要创建B的bean实例,但是B也是要在构造函数中注入A,即B(A a),此时B需要在Map中找到不完整的A,但是发现找不到,因为A的Bean实体还没创建完(还在等着B)。
  • 注意:Spring容器是按照字母的顺序创建 Bean的,因此循环依赖中,字母排在前面的Bean不能采用构造函数注入。

Sping解决循环依赖全流程

首先了解Spring bean相关的三个Map

  • singletonObject,存放所有创建完毕的单例bean(完整的bean,即已经完成实例化并进行属性填充)。
  • earlySingletonObjects,存放仅完成实例化,但未进行属性填充和初始化的Bean。
  • singletonfactories,存放能创建Bean的工厂,通过这个工厂能获得bean,延迟bean生成,工厂生成的bean会放到earlySingletonObjects中。

在实例化bean,Spring是不知道当前bean有没有循环依赖的,它会义无反顾的往singletonfactories中存放当前bean的工厂,这个步骤就是提前曝光

然后开始属性注入,此时bean A发现要注入bean B,所以请执行getBean(B)

  • 先去singletonObject里找有没有,如果有则进行返回
  • 如果没有,则判断Bean是否在创建中,如果不在创建中,则返回null
  • 如果在创建中,则去earlySingletonObjects找,如果有则进行返回
  • 如果没有,则去singletonfactories找到这个bean的工厂,通过工厂去创建bean,并存放到earlySingletonObjects中
  • 如果singletonfactories没有找到bean的工厂就返回null
  • 如果返回null,说明bean还没有创建,这个时候会先把这个bean标记为创建中,再调用doCreateBean(即,实例化,属性填充,初始化三个步骤)

此时就到了B这个bean属性注入的步骤了,调用了getBean(A),A此时在singletonfactories中找到提前暴露的工厂的到了A,然后把A从singletonfactories中删除,放到earlySingletonObjects中。

此时B属性注入成功,然后进行初始化,最后B存放到singletonObject中。

此时又回到了A注入B的地方,完成了对B的注入,然后A也从earlySingletonObjects删除,存放到singletonObject中。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • SpringBatch从入门到精通之StepScope作用域和用法详解

    SpringBatch从入门到精通之StepScope作用域和用法详解

    这篇文章主要介绍了SpringBatch从入门到精通之StepScope作用域和用法详解,主要包括IOC容器中几种bean的作用范围以及可能遇到的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-05-05
  • Java返回分页结果集的封装代码实例

    Java返回分页结果集的封装代码实例

    这篇文章主要介绍了java返回分页结果集的封装代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • java使用MulticastSocket实现基于广播的多人聊天室

    java使用MulticastSocket实现基于广播的多人聊天室

    这篇文章主要为大家详细介绍了java使用MulticastSocket实现基于广播的多人聊天室,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-01-01
  • SpringBoot整合Jackson的过程详解

    SpringBoot整合Jackson的过程详解

    这篇文章给大家介绍了SpringBoot整合Jackson的整合过程,文中通过代码示例给给大家介绍的非常详细,并附带附工具类与使用案例,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2023-12-12
  • SpringBoot响应处理之以Json数据返回的实现方法

    SpringBoot响应处理之以Json数据返回的实现方法

    这篇文章主要介绍了SpringBoot整合Web开发其中Json数据返回的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-09-09
  • 使用IDEA配置Mybatis-Plus框架图文详解

    使用IDEA配置Mybatis-Plus框架图文详解

    这篇文章主要介绍了使用IDEA配置Mybatis-Plus框架,本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • Java8实战之Stream的延迟计算

    Java8实战之Stream的延迟计算

    JDK中Stream的中间函数如 filter(Predicate super T>)是惰性求值的,filter并非对流中所有元素调用传递给它的Predicate,下面这篇文章主要给大家介绍了关于Java8实战之Stream延迟计算的相关资料,需要的朋友可以参考下
    2021-09-09
  • Java如何导出数据库中的所有数据表到指定文件夹

    Java如何导出数据库中的所有数据表到指定文件夹

    这篇文章主要介绍了Java导出数据库中的所有数据表到指定文件夹,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • Java程序中的延迟加载功能使用

    Java程序中的延迟加载功能使用

    这篇文章主要介绍了Java程序中的延迟加载功能使用,一定程度上有助于提升性能和降低内存使用率,需要的朋友可以参考下
    2015-07-07
  • SpringMVC处理数据输出的实例代码

    SpringMVC处理数据输出的实例代码

    这篇文章主要给大家介绍了关于SpringMVC处理数据输出的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05

最新评论