依赖注入 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 为例:
创建 Bean A
Spring 容器开始创建 Bean A,首先将 Bean A 对应的ObjectFactory放入三级缓存singletonFactories中,此时 Bean A 处于实例化但未初始化的状态。
发现依赖 Bean B
在初始化 Bean A 的过程中,发现 Bean A 依赖 Bean B,于是 Spring 容器开始创建 Bean B。
创建 Bean B
和创建 Bean A 类似,Spring 容器将 Bean B 对应的ObjectFactory放入三级缓存singletonFactories中,此时 Bean B 也处于实例化但未初始化的状态。
发现依赖 Bean A
在初始化 Bean B 的过程中,发现 Bean B 依赖 Bean A,这时 Spring 容器会尝试从缓存中获取 Bean A。
从缓存中获取 Bean A
Spring 容器首先从一级缓存singletonObjects中查找 Bean A,发现没有;接着从二级缓存earlySingletonObjects中查找,也没有;最后从三级缓存singletonFactories中找到 Bean A 对应的ObjectFactory,通过ObjectFactory创建一个早期的 Bean A 实例,并将其从三级缓存移到二级缓存earlySingletonObjects中。
完成 Bean B 的创建
将早期的 Bean A 实例注入到 Bean B 中,完成 Bean B 的初始化,并将 Bean B 放入一级缓存singletonObjects中,同时从二级缓存和三级缓存中移除。
完成 Bean A 的创建
将已经初始化好的 Bean B 注入到 Bean A 中,完成 Bean A 的初始化,并将 Bean A 放入一级缓存singletonObjects中,同时从二级缓存和三级缓存中移除。

提示:Spring 在创建 Bean 时默认会根据自然排序进行创建,所以 A 会先于 B 进行创建
注意
仅适用于单例模式:Spring 的三级缓存机制只对单例模式的 bean 有效,对于原型模式(
prototype)的 bean,Spring 无法解决循环依赖问题。构造函数注入无法解决:如果使用构造函数注入形成循环依赖,Spring 无法解决,因为在实例化时就需要完整的依赖对象。
Spring 不能解决“A的构造方法中依赖了 B 的实例对象,同时 B 的构造方法中依赖了 A 的实例对象”这类问题,但可以通过 @Lazy 注解解决。