了解Spring中循環(huán)依賴流程,絕殺面試官!
請闡述下你對spring循環(huán)依賴的理解?真的是......禿頭是有原因的......
下面逐層深入了解,揭開它的神秘面紗!
一、什么是循環(huán)依賴
二、相關概念說明
- spring中的一、二、三級緩存
#一級緩存:存儲所有創(chuàng)建完整的bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
#二級緩存:存儲完成實例化后的bean(createBeanInstance方法執(zhí)行結束,但還未執(zhí)行populateBean-屬性注入等方法)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
#三級緩存:對象工廠,通過ObjectFactory.getObject()方法,獲取到類似于二級緩存中存儲的對象
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
- spring bean初始化關鍵流程說明
createBeanInstance:推斷構造方法,通過反射,實例化一個對象;執(zhí)行完該方法,會調用addSingletonFactory方法,將之放入三級緩存中。
// 三級緩存中存儲的是對象工廠,獲取對象時,需調用ObjectFactory.getObject(方法)
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
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);
}
}
getEarlyBeanReference方法:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
populateBean:為上述創(chuàng)建的對象填充屬性信息。
initializeBean:執(zhí)行aware接口、初始化方法、AOP代理【如有需要】。
三、都說spirng已經(jīng)解決了循環(huán)依賴,那spring可以解決什么情況下的循環(huán)依賴?
A、B相互依賴場景 | 是否支持 |
均采用setter方法/屬性注入 | 支持 |
均采用構造器注入 | 不支持 |
A中注入B為setter方法,B中注入A為構造器 | 支持 |
A中注入B為構造器方法,B中注入A為setter方法 | 不支持 |
四、為什么說spring只解決了部分情況的循環(huán)依賴?
在調用createBeanInstance,通過反射實例化對象后,會調用addSingletonFactory方法,將創(chuàng)建的早期對象存放到三級緩存中。所以關鍵在于三級緩存中是否存在早期對象;比如:上述場景二:均采用構造器注入,為什么不支持該場景呢?
創(chuàng)建beanA時,在執(zhí)行createBeanInstance(beanA)方法時,此時發(fā)現(xiàn)beanA依賴beanB,
則會去執(zhí)行創(chuàng)建beanB流程,但是此時addSingletonFactory方法并沒有執(zhí)行,
則三級緩存中不存在早期對象beanA,所以spring不支持“均采用構造器注入”的場景。
上述其他場景不再一一闡述。
五、只使用二級緩存可以解決循環(huán)依賴嗎?
AOP代理本質是反射,反射出來的對象每次都是不同的,如果多個對象和beanA出現(xiàn)循環(huán)依賴,那么只有二級緩存的話就會反射出不同的對象了。