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。
- 首先,Spring容器将A对象完成实例化,然后,将A对象加入singletonFactories中 (注意这里,还未完成初始化,就将A放入了。)
- 接着,对A对象进行依赖注入,发现自己依赖对象B,此时就尝试去get(B)
- 发现B还没有被实例化,对B进行实例化
- 然后B在初始化的时候发现自己依赖了对象A,于是尝试get(A)。
- 先在singletonObjects找,再去earlySingletonObjects找,都没有找到。最后去singletonFactories中找到了。
- 然后,通过singletonFactories生成A对象,放入earlySingletonObjects中,并从singletonFactories中移除A。
- B完成依赖注入后,执行初始化。此时,将B对象放入到singletonObjects中。
- 此时,A对象获取到B,完成依赖注入,初始化,最后将A放入singletonObjects中,从earlySingletonObjects中移除A。
Spring 容器创建或初始化对象的核心步骤
-
createBeanInstance Bean实例化
-
判断是否早期暴露对象或正在创建的对象,执行addSingletonFactory
-
populateBean 依赖注入Bean
-
initializeBean 初始化Bean