Spring循环依赖的三种方式
依赖场景:A->B->A 或者 A->B->C->A
1.理解spring循环依赖的原理,首先了解一下Spring bean在容器中创建到销毁的若干阶段。
单例bean循环依赖体现在实例化和填充属性两个阶段,对应构造器和set方式(Singleton、Prototype)循环依赖。
2.Spring解决循环依赖主要使用了其三级缓存:
// singletonObjects 主要缓存初始化完成的单例对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
// earlySingletonObjects用户缓存提前暴露的单例对象的Cache,只进行实例化未初始化完成
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
// singletonFactories 单例对象工厂的缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
3.获取单例bean的方法
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
isSingletonCurrentlyInCreation()判断当前单例bean是否正在创建中,也就是没有初始化完成(比如A的构造器依赖了B对象所以得先去创建B对象, 或者在A的populateBean过程中依赖了B对象,得先去创建B对象,这时的A就是处于创建中的状态。
allowEarlyReference 是否允许从singletonFactories中通过getObject拿到对象
// 如果设置了单例bean提前暴露,会调用addSingletonFactory方法。
boolean earlySingletonExposure = (mbd.isSingleton()
&& this.allowCircularReferences
&& isSingletonCurrentlyInCreation(beanName));
addSingletonFactory(beanName, new ObjectFactory() {
public Object getObject () throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
// 此处getEarlyBeanReference通过SmartInstantiationAwareBeanPostProcessor执行无参构造方法创建对象。
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
Spring不能解决构造方法的循环依赖,能解决set方式(singleton)依赖。prototype作用域bean,不论set和构造器方式,Spring容器无法完成依赖注入,因为Spring容器不进行缓存"prototype"作用域的bean,因此无法提前暴露一个创建中的bean。