Spring---循环依赖

总结

什么是循环依赖?  

  A对象中有属性B对象,B对象中有属性A对象;

Spring如何解决循环依赖?

  1、将Bean的创建分成 实例化 + 初始化 2个过程;

  2、提供了3级缓存维护单例Bean:    

  • singletonObjects:保存已经初始化完毕的单例Bean。
  • earlySingletonObjects:保存已经被实例化但未完全初始化的Bean。  这些Bean是为了解决循环依赖而提前暴露出来的。
  • singletonFactories:保存用来生成Bean的ObjectFactory。  当需要提前暴露Bean时,会从这里获取对应的ObjectFactory并调用getObject()方法得到Bean的早期引用。

 

  1. 创建Bean实例:

    • Spring首先尝试创建一个Bean A的实例。
    • 如果发现Bean A依赖于Bean B,那么Spring会暂停A的初始化,并开始创建Bean B。
  2. 提前暴露早期引用:

    • 创建Bean B的过程中,如果B又依赖于A,Spring会在singletonFactories为A创建一个ObjectFactory,并将这个工厂放入singletonFactories缓存。
    • 然后Spring从singletonFactories获取A的ObjectFactory并调用其getObject()方法得到A的一个早期引用(未完全初始化的对象),并将这个早期引用放入earlySingletonObjects缓存。
    • 使用A的早期引用继续B的初始化过程。
  3. 完成依赖注入:

    • B完成初始化后,它会被放入singletonObjects缓存
    • 接着,Spring返回到A的初始化过程,完成A的属性注入和其他初始化工作。
  4. 完成Bean A的初始化:

    • 一旦A的所有依赖都已注入,并且所有初始化回调(如@PostConstruct注解的方法或InitializingBean.afterPropertiesSet()等)都执行完毕,A就被认为是完全初始化的。
    • 完全初始化后的A从earlySingletonObjects移除,并被放入singletonObjects缓存,成为完整的单例Bean。

 

当Spring尝试创建一个Bean A时,如果发现A依赖于Bean B,而B又依赖于A,那么Spring会执行以下步骤:

  1. 创建Bean A的实例,此时A处于创建中状态。
  2. 尝试创建Bean B的实例,同样B也处于创建中状态。
  3. 在创建B的过程中,发现它依赖于A,但由于A已经在创建过程中,Spring会从singletonFactories中获取A的ObjectFactory,然后调用getObject()方法得到A的早期引用。
  4. 使用A的早期引用完成B的初始化。
  5. 完成A的初始化,将其放入singletonObjects中。

  

  3、在Spring中,提供的DI方式中:   

  • 构造器注入:如果使用构造器注入(constructor injection),Spring将无法解决循环依赖问题,因为每个Bean都需要另一个完全初始化的Bean作为其构造参数。在这种情况下,Spring会抛出BeanCurrentlyInCreationException
  • Setter注入(属性注入&setter注入):Spring可以使用setter注入(field injection)来解决循环依赖问题。当一个Bean正在被创建但尚未完成时,Spring会提前暴露这个Bean的一个早期引用(early reference)。这样,其他依赖它的Bean就可以获得这个早期引用,并继续它们的初始化过程。

  

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
     *  what?
     *      A对象中有属性B对象,B对象中有属性A对象;
     *      创建任何对象,都是先实例化,后初始化;
     *
     *  如何解决循环依赖?
     *      1、实例化(createBeanInstance) 与 初始化(populateBean,initializeBean) 分开;
     *      2、设置缓存预存对象(先将 先实例化的A对象 缓存起来,在B对象初始化设置属性A时,从 缓存中获取A实例,进行赋值);
     *
     *  使用半成品对象会有问题,如何解决?
     *      spring3级缓存:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
     *
     *          1级缓存:
     *              private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
     *                  k:beanName
     *                  v:bean
     *
     *          2级缓存:
     *              private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
     *                  k:beanName
     *                  v:本成品bean
     *
     *          3级缓存:
     *              private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
     *                  k:beanName
     *                  v:lambada表达式
     *
     */

  

  

  

  

  

posted on   anpeiyong  阅读(40)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2020-03-23 JavaSE---进制

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示