Spring如何解决循环依赖问题?
getSingleton(String beanName)
在Spring的doGetBean()方法中的第一次调用getSingleton方法(也就是getSingleton(String beanName)方法)中,反映了Spring中针对循环依赖的解决思想。
当Spring容器初始化时,对于每一个声明为单例的Bean,Spring都会创建一个对应的ObjectFactory并将其存储在singletonFactories映射中。在这个映射中,beanName作为键,对应的ObjectFactory作为值。因此,当需要获取一个单例Bean的实例时,Spring会首先尝试从singletonObjects映射中获取该实例。如果该实例不存在,并且允许早期引用(allowEarlyReference为true),那么Spring会从earlySingletonObjects映射中获取该实例。如果仍不存在,Spring会从singletonFactories映射中获取对应的ObjectFactory,并调用其getObject()方法来创建并返回该Bean的实例。
这个ObjectFactory和earlySingletonObjects就是解决循环依赖问题的关键!
// Bean实例的缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从缓存中获取
Object singletonObject = this.singletonObjects.get(beanName);
// 若缓存内不存在且当前正在创建该单例Bean
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 从早期缓存中获取单例实例
singletonObject = this.earlySingletonObjects.get(beanName);
// 若早期缓存中不存在且允许早期引用
if (singletonObject == null && allowEarlyReference) {
// 获取对应的ObjectFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 获取该ObjectFactory对应的实例
singletonObject = singletonFactory.getObject();
// 填充早期缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 删除对应的ObjectFactory
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
在这个getSingleton
方法中,Spring使用了几个关键的策略和技术来解决循环依赖的问题。
- 三级缓存机制:Spring使用了三级缓存机制,即
singletonObjects
、earlySingletonObjects
和singletonFactories
。这三个Map结构用于存储不同阶段的单例Bean。singletonObjects
:存储完全初始化好的Bean。earlySingletonObjects
:存储Bean的早期引用,也就是说Bean已经被实例化了,但是还没进行属性填充和初始化方法。singletonFactories
:存储创建Bean的工厂对象,也就是ObjectFactory。
- 检查当前Bean是否在创建中:
isSingletonCurrentlyInCreation(beanName)
方法用于检查当前Bean是否正在被创建。如果返回true,说明存在循环依赖。 - 同步代码块:当检测到循环依赖时,Spring使用了同步代码块来确保线程安全。这避免了在多线程环境下可能出现的并发问题。
- 从
singletonFactories
中获取ObjectFactory:如果当前Bean正在创建中并且允许早期引用,Spring会从singletonFactories
中获取该Bean的ObjectFactory。 - 通过ObjectFactory创建Bean的早期引用:然后调用ObjectFactory的
getObject()
方法来创建一个该Bean的早期引用,并将其放入earlySingletonObjects
缓存中。同时从singletonFactories
中移除该ObjectFactory,确保每个Bean只被创建一次。 - 解决循环依赖:通过这种机制,即使Bean还没有完全初始化完成,其他依赖于它的Bean也可以通过
earlySingletonObjects
获取到它的早期引用,从而解决了循环依赖的问题。
总结来说,Spring通过三级缓存机制、同步代码块和ObjectFactory的配合使用,巧妙地解决了循环依赖的问题。这确保了每个Bean只被创建一次,并且在解决依赖关系时线程安全。