Spring是如何解决Bean之间的循环依赖

Bean的作用域

在 Spring 框架中,Bean 的作用域(Scope)定义了 Spring 容器对 Bean 实例化和管理的方式。不同的 Bean 作用域会影响 Bean 的生命周期和在应用程序中的可见性。以下是 Spring 支持的一些主要 Bean 作用域:

作用域名称 描述
singleton (单例) 默认作用域。对于在Spring IoC容器中定义为singleton的Bean,容器将只创建一个Bean实例。
prototype (原型) 对于定义为prototype的Bean,每次请求都会创建一个新的Bean实例。
request 每次HTTP请求都会创建一个新的Bean,仅适用于Web应用程序。
session 每个HTTP Session都会创建一个新的Bean,仅适用于Web应用程序。
application 在Web应用程序中,每个ServletContext都会创建一个Bean实例。
websocket 在WebSocket生命周期内,每个WebSocket都会创建一个Bean实例。
自定义 通过实现BeanFactory的registerScope方法来定义自己的作用域。

Spring 支持哪种循环依赖?

模式 依赖注入方式 是否支持
singleton 构造器注入 否
singleton 属性注入 是
prototype 构造器注入 否
prototype 属性注入 否

Spring 只支持单例模式下属性注入的循环依赖,其它类型的循环依赖,Spring 处理不了的,会直接抛出BeanCurrentlylnCreationException异常

Spring 是如何实现支持单例模式下属性注入的循环依赖

在Spring的DefaultSingletonBeanRegistry类中,有以下三个Map,它正是通过这三个属性来实现支持循环依赖的:

  • singletonObjects:存储已完成初始化的单例对象map

  • earlySingletonObjects:存储已完成实例化但未初始化的单例对象map

  • singletonFactories:存储单例对象工厂map

示例:假设A依赖了B的实例对象,同时B也依赖A的实例对象。Spring 容器先创建的对象是A。

  1. 首先,Spring容器将A对象完成实例化,然后,将A对象加入singletonFactories中 (注意这里,还未完成初始化,就将A放入了。)
  2. 接着,对A对象进行依赖注入,发现自己依赖对象B,此时就尝试去get(B)
  3. 发现B还没有被实例化,对B进行实例化
  4. 然后B在初始化的时候发现自己依赖了对象A,于是尝试get(A)。
  5. 先在singletonObjects找,再去earlySingletonObjects找,都没有找到。最后去singletonFactories中找到了。
  6. 然后,通过singletonFactories生成A对象,放入earlySingletonObjects中,并从singletonFactories中移除A。
  7. B完成依赖注入后,执行初始化。此时,将B对象放入到singletonObjects中。
  8. 此时,A对象获取到B,完成依赖注入,初始化,最后将A放入singletonObjects中,从earlySingletonObjects中移除A。
理解过程建议结合BeanFactory的抽象实现类AbstractBeanFactory与参考链接中的图片解析。从源码方法createBean或者下级实现doCreateBean开始解读。

Spring 容器创建或初始化对象的核心步骤

  1. createBeanInstance Bean实例化

  2. 判断是否早期暴露对象或正在创建的对象,执行addSingletonFactory

  3. populateBean 依赖注入Bean

  4. initializeBean 初始化Bean

参考

posted @ 2024-08-13 18:17  抒写  阅读(64)  评论(0)    收藏  举报