循环依赖问题
参考文章:
bean的循环依赖解决过程
顺序为,实例化、属性赋值(属性填充)、初始化、依赖注入。
实例化 Bean: Spring 在实例化 Bean 时,会先创建一个空的 Bean 对象,并将其放入一级缓存中。
属性赋值: Spring 开始对 Bean 进行属性赋值,如果发现循环依赖,会将当前 Bean 对象提前暴露给后续需要依赖的 Bean(通过提前暴露的方式解决循环依赖)
初始化 Bean: 完成属性赋值后,Spring 将 Bean 进行初始化,并将其放入二级缓存中。
注入依赖: Spring 继续对 Bean 进行依赖注入,如果发现循环依赖,会从二级缓存中获取已经完成初始化的 Bean 实例。
三级缓存如何解决循环依赖
Spring可以解决使用setter注入(也就是使用注解注入的方式)的循环依赖问题。
因为spring解决循环依赖本质上是通过bean的不同加载阶段使用的不同缓存来解决的。主要就依靠于singletonFactories这个第三级缓存来解决。
spring是没有办法解决使用构造器注入方式导致的循环依赖问题的,因为bean要加入singletonFactories
三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。
如果A对象依赖于B对象。第三级缓存是通过A的工厂对象获取到的A的早期引用,将这个早期引用的占位符提前暴露给B对象而解决的循环依赖问题。
bean在加载过程的顺序:
- 先从
一级缓存singletonObjects
中去获取。(如果获取到就直接return) - 如果获取不到或者对象正在创建中(
isSingletonCurrentlyInCreation()
),那就再从二级缓存earlySingletonObjects
中获取。(如果获取到就直接return) - 如果还是获取不到,且允许singletonFactories(allowEarlyReference=true)通过
getObject()
获取。就从三级缓存singletonFactory
.getObject()获取。(如果获取到了就从singletonFactories
中移除,并且放进earlySingletonObjects
。其实也就是从三级缓存移动(是剪切、不是复制哦~)
到了二级缓存)
getSingleton()
从缓存里获取单例对象步骤分析可知,Spring解决循环依赖的诀窍:就在于singletonFactories这个第三级缓存。这个Cache里面都是ObjectFactory
,它是解决问题的关键。
通过这样的三级缓存延迟初始化机制可以解决我们的循环以来问题。
三级缓存
三级缓存的
一级缓存
- 数据结构:
Map<String, Object>
,键为bean的名称,值为已初始化的bean实例。 - 描述:存储完全初始化好的bean,可以直接使用。
- 位置:
DefaultSingletonBeanRegistry
类中的singletonObjects
属性。
二级缓存
- 数据结构:
Map<String, Object>
,键为bean的名称,值为早期的bean引用。 - 描述:存储已实例化但未完全初始化的bean,主要用于解决循环依赖。
- 位置:
DefaultSingletonBeanRegistry
类中的earlySingletonObjects
属性。
三级缓存
- 数据结构:
Map<String, ObjectFactory<?>>
,键为bean的名称,值为生成早期bean引用的工厂对象。 - 描述:存储ObjectFactory对象,用于在创建bean时解决循环依赖。
- 位置:
DefaultSingletonBeanRegistry
类中的singletonFactories
属性。
存储一个ObjectFactorty对象,用于创建并返回需要获取对象的早期地址。
- 三级缓存中的早期引用确实是指向正在创建的
A
的内存地址,但它是一个不完整的占位符。 - 一旦
A
完全初始化,后续的依赖将会使用A
的实际引用,而不是早期引用。这样确保了在循环依赖的情况下,所有依赖关系能够正确解决。
Spring中使用的设计模式
- 单例模式(Singleton Pattern):
- 确保一个类只有一个实例,并提供全局访问点。Spring的bean默认是单例的。
- 工厂模式(Factory Pattern):
- 定义一个用于创建对象的接口,让子类决定实例化哪一个类。Spring通过
ObjectFactory
实现了这一模式。
- 定义一个用于创建对象的接口,让子类决定实例化哪一个类。Spring通过
- 代理模式(Proxy Pattern):
- 为其他对象提供一种代理以控制对这个对象的访问。Spring AOP使用代理模式来实现切面编程。
- 模板方法模式(Template Method Pattern):
- 在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中实现。Spring的JdbcTemplate就是这种模式的一个例子。
- 观察者模式(Observer Pattern):
- 定义了一种一对多的依赖关系,使得当一个对象改变状态时,所有依赖于它的对象都得到通知并自动更新。Spring的事件机制利用了这一模式。