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  抒写  阅读(18)  评论(0编辑  收藏  举报