在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中。

总结

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

相关文章

  • springboot如何配置嵌套map和list参数

    springboot如何配置嵌套map和list参数

    这篇文章主要介绍了springboot如何配置嵌套map和list参数问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-03-03
  • 浅析Java中的虚拟线程

    浅析Java中的虚拟线程

    在本篇文章中,小编将带大家深入了解Java虚拟线程的原理、如何使用、使用的注意事项以及其他相似技术的差别,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-10-10
  • java外卖订餐系统小项目

    java外卖订餐系统小项目

    这篇文章主要为大家详细介绍了java外卖订餐系统小项目,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-01-01
  • 在SpringBoot中通过jasypt进行加密解密的方法

    在SpringBoot中通过jasypt进行加密解密的方法

    今天小编就为大家分享一篇关于在SpringBoot中通过jasypt进行加密解密的方法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • Java中实现双数组Trie树实例

    Java中实现双数组Trie树实例

    这篇文章主要介绍了Java中实现双数组Trie树实例,双数组Trie就是一种优化了空间的Trie树,本文给出了实现代码、测试代码和测试结果,需要的朋友可以参考下
    2015-01-01
  • 聊聊Arrays.deepToString()和Arrays.toString()的区别

    聊聊Arrays.deepToString()和Arrays.toString()的区别

    这篇文章主要介绍了聊聊Arrays.deepToString()和Arrays.toString()的区别,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • Java基本数据类型之间的相互转换详解

    Java基本数据类型之间的相互转换详解

    这篇文章主要讲解Java中基本数据类型的转换,数据之间相互转换是经常会用到的基础操作,文中讲的很清晰,希望能给大家做一个参考。
    2022-05-05
  • 浅谈Java注解和动态代理

    浅谈Java注解和动态代理

    这篇文章主要介绍了Java中有关注解和动态代理的一些知识,涉及了Annotation、数据类型等相关内容,需要的朋友可以参考下。
    2017-09-09
  • SpringBoot3 响应式网络请求客户端的实现

    SpringBoot3 响应式网络请求客户端的实现

    本文主要介绍了SpringBoot3 响应式网络请求客户端的实现,文章详细阐述了如何使用SpringBoot3的网络请求客户端进行HTTP请求和处理响应,并提供了示例代码和说明,具有一定的参考价值,感兴趣的可以了解一下
    2023-08-08
  • 使用@pathvariable与@requestparam碰到的一些问题及解决

    使用@pathvariable与@requestparam碰到的一些问题及解决

    这篇文章主要介绍了使用@pathvariable与@requestparam碰到的一些问题及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08

最新评论