依赖注入 DI

注入方式

  • 构造器注入

  • 字段注入

  • setter 注入

注解

  • @Autowired:先按类型查找,若存在多个相同类型的 Bean,再结合@Qualifier按名称查找。

  • @Resource:先按名称查找,若找不到匹配的 Bean,再按类型查找。

注意:@Resource 注解本身不支持构造器注入,若需要进行构造器注入,建议使用 @Autowired 注解。

循环依赖问题

https://blog.csdn.net/cy973071263/article/details/132676795

原理

Spring 主要通过三级缓存机制来解决循环依赖问题:

  • 一级缓存(singletonObjects:单例对象缓存,存放已经完全初始化好的单例 bean。

  • 二级缓存(earlySingletonObjects:早期曝光的单例对象缓存,存放提前暴露的单例 bean,这些 bean 还未完成初始化。(从三级缓存当中产生出来的对象)

  • 三级缓存(singletonFactories:单例对象工厂缓存,存放原始对象对应的 ObjectFactory。通过调用 getObject() 方法,就能够在需要动态代理的情况下为原始对象生成代理对象并返回,否则返回原始对象,以此来处理循环依赖时还需要动态代理的情况。会将原始对象(的代理对象)放入二级缓存。

以 Bean A 依赖 Bean B,Bean B 又依赖 Bean A 为例:

  1. 创建 Bean A

Spring 容器开始创建 Bean A,首先将 Bean A 对应的ObjectFactory放入三级缓存singletonFactories中,此时 Bean A 处于实例化但未初始化的状态。

  1. 发现依赖 Bean B

在初始化 Bean A 的过程中,发现 Bean A 依赖 Bean B,于是 Spring 容器开始创建 Bean B。

  1. 创建 Bean B

和创建 Bean A 类似,Spring 容器将 Bean B 对应的ObjectFactory放入三级缓存singletonFactories中,此时 Bean B 也处于实例化但未初始化的状态。

  1. 发现依赖 Bean A

在初始化 Bean B 的过程中,发现 Bean B 依赖 Bean A,这时 Spring 容器会尝试从缓存中获取 Bean A。

  1. 从缓存中获取 Bean A

Spring 容器首先从一级缓存singletonObjects中查找 Bean A,发现没有;接着从二级缓存earlySingletonObjects中查找,也没有;最后从三级缓存singletonFactories中找到 Bean A 对应的ObjectFactory,通过ObjectFactory创建一个早期的 Bean A 实例,并将其从三级缓存移到二级缓存earlySingletonObjects中。

  1. 完成 Bean B 的创建

将早期的 Bean A 实例注入到 Bean B 中,完成 Bean B 的初始化,并将 Bean B 放入一级缓存singletonObjects中,同时从二级缓存和三级缓存中移除。

  1. 完成 Bean A 的创建

将已经初始化好的 Bean B 注入到 Bean A 中,完成 Bean A 的初始化,并将 Bean A 放入一级缓存singletonObjects中,同时从二级缓存和三级缓存中移除。

提示:Spring 在创建 Bean 时默认会根据自然排序进行创建,所以 A 会先于 B 进行创建

依赖情况

依赖注入方式

循环依赖是否被解决

AB相互依赖(循环依赖)

均采用setter方法注入

AB相互依赖(循环依赖)

均采用构造器注入

AB相互依赖(循环依赖)

A中注入B的方式为setter方法,B中注入A的方式为构造器

AB相互依赖(循环依赖)

B中注入A的方式为setter方法,A中注入B的方式为构造器

注意

  • 仅适用于单例模式:Spring 的三级缓存机制只对单例模式的 bean 有效,对于原型模式(prototype)的 bean,Spring 无法解决循环依赖问题。

  • 构造函数注入无法解决:如果使用构造函数注入形成循环依赖,Spring 无法解决,因为在实例化时就需要完整的依赖对象。

Spring 不能解决“A的构造方法中依赖了 B 的实例对象,同时 B 的构造方法中依赖了 A 的实例对象”这类问题,但可以通过 @Lazy 注解解决。

我也放荡不羁爱自由!