Spring到底是如何解决循环依赖的?
Q:spring如何解决循环依赖的问题?
A:三级缓存。
spring中的循环依赖有三种情况,构造器之间的循环依赖,构造器和setter之间的循环依赖,setter/field之间的循环依赖,spring只能解决setter/field之间的循环依赖。三级缓存指的是singletonObjects、earlySingletonObjects和singletonFactories,singletonObjects用于存储完整的单例对象,earlySingletonObjects和singletonFactories用来存储提前曝光的、没有完成所有初始化流程的单例对象,网上的很多博客都没有把”提前曝光“这个东西说清楚,以至于搞不清楚为啥要整三级缓存,二级缓存不是也能解决问题么。
循环依赖发生在创建bean对象的过程中,也就是调用BeanFactory的getBean()方法的时候,getBean()的整体逻辑可以分为两条线来看,一条是doGetBean(),一条是createBean(),虽然这两条线都属于getBean(),但是spring非常巧妙的用第三极缓存和一个匿名内部类进行了解耦,高手就是高手,我等菜鸡只能高手指。
先看看代码逻辑,有点多,可以在idea中对照spring的源码跟一下。
第一条线:getBean()
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
/**
这里,一进来直接尝试从缓存中获取,如果从一级缓存中拿到了,
就返回,否则从二级缓存中那,如果没拿到,再尝试从三级缓存中
拿,如果拿到了,就把它从三级缓存提升到二级缓存,
这个方法是解决循环依赖的第一个关键逻辑。①
*/
Object sharedInstance = this.getSingleton(beanName);
if (mbd.isSingleton()) {
/**
这里的getSingleton方法封装了doGetBean()这第一条线的
整体逻辑,接下面的方法。② */
sharedInstance = this.getSingleton(beanName, () -> {
try {
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
this.destroySingleton(beanName);
throw var5;
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
//①
public Object getSingleton(String beanName) {
return this.getSingleton(beanName, true);
}
//①
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
synchronized(this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
/**
这里就是向三级缓存中
*/
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
//②
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
...其他造成
//singletionFactory上一层调用传过来的匿名内部类
/**
这里调用doCreateBean(beanName)方法,在这个方法中,为这个bean向三级缓存中添加了一个
ObjectFactory,然后进行属性赋值和初始化操作
*/
singletonObject = singletonFactory.getObject();
..其他操作
//添加到一级缓存中
this.addSingleton(beanName, singletonObject);
return singletonObject;
}
}
第二条线:createBean()
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
....
/**
将实例对象曝光到三级缓存singletonFactories中,
这里是用一个ObjectFactory对象保存了通过反射创建的实例对象
这里是解决循环依赖的第二个关键点 ③
**/
this.addSingletonFactory(beanName, () -> {
//调用这个匿名内部类时,在返回原对象的时候
//扫描了IoC容器中所有的SmartInstantiationAwareBeanPostProcessor
//并指定了对应的callback方法getEarlyBeanReference
//这是spring的一个扩展点
return this.getEarlyBeanReference(beanName, mbd, bean);
});
}
//③
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized(this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
//将上面的Objectfactory类型的匿名内部类放到一级缓存
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
Iterator var5 = this.getBeanPostProcessors().iterator();
while(var5.hasNext()) {
BeanPostProcessor bp = (BeanPostProcessor)var5.next();
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor)bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
下面用一个例子来说明一下三级缓存解决循环依赖的过程,假设有两个类A和B,通过setter/field进行循环依赖,当试图通过getBean(A)方法获取A的实例对象时,首先尝试从缓存中拿,此时三个缓存中都没有,然后就会调用第一个匿名ObjectFactory的getObject方法,触发对createBean(beanName)的调用,在createBean中首先通过反射获取到类的实例对象,然后将其封装到一个ObjectFactory中并放入到三级缓存,然后按照Bean的初始化流程进行属性赋值(@Autowired),此时发现其依赖于B对象,然后再调用getBean(B)尝试获取B对象,B对象在执行到属性赋值的时候发现依赖于A,再调用getBean(A),关键的地方来了,此时由于A已经在三级缓存singletonFactories中了,所以就会从三级缓存中拿到那个匿名的ObjectFactory,然后调用getObject()得到没有初始化完成的A对象,然后第二个匿名的ObjectFactory触发三级升二级,此时A就在二级缓存中了,紧接着B完成整个初始化流程,最终被放到一级缓存singletonObjects中,A的初始化过程从getBean(B)中返回,继续执行,完成初始化和生命周期hook的调用,最终被放到一级缓存singletonObjects中,循环依赖的问题的解决了。
spring解决循环依赖其实就是利用了java bean对象实例化的时候可先不对属性进行赋值的特点,利用三级缓存把循环依赖中断,使其中一个对象完成初始化,再去初始化另外一个。
二级缓存和三级缓存从功能上没啥区别,只不过期间要调用spring的一个扩展点接口的方法,如果没有这一步,我觉得完全可以把这两个缓存合并成一个。