背景
bean的生命周期如下圖所示:
image.png
@PostConstruct注解是會被一個專門的BeanPostProcessor接口的具體實現類來處理的,實現類是:InitDestroyAnnotationBeanPostProcessor。
按照加載順序,@PostConstruct也會按照依賴順序執行。
但是在代碼里并沒有按照期望順序執行,依賴關系如下:
@Service
@Slf4j
public class A {
@Resource
private B b;
@PostConstruct
public void init(){
log.info("A PostConstruct");
b.test();
}
}
@Service
@Slf4j
public class B {
@Resource
private C c;
@PostConstruct
public void init(){
log.info("B PostConstruct");
}
public void test(){
log.info("B Test");
}
}
@Service
public class C {
@Resource
private A a;
}
A對象里依賴了B對象,并且A的@PostConstruct方法依賴了B的@PostConstruct生成的數據,但是A的@PostConstruct,導致拿到的數據為空
問題分析
經過debug,發現是由于循環依賴導致,B先加載并且提前暴露,導致A執行@PostConstruct時B還沒有初始化完成
為解決循環依賴,spring采用三級緩存機制,將實例化成功的對象加載到三級緩存,
這三級緩存的作用分別是:
singletonFactories : 進入實例化階段的單例對象工廠的cache (三級緩存)
earlySingletonObjects :完成實例化但是尚未初始化的,提前暴光的單例對象的Cache (二級緩存)
singletonObjects:完成初始化的單例對象的cache(一級緩存)
我們在創建bean的時候,會首先從cache中獲取這個bean,這個緩存就是sigletonObjects。主要的調用方法是:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
//isSingletonCurrentlyInCreation()判斷當前單例bean是否正在創建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
//allowEarlyReference 是否允許從singletonFactories中通過getObject拿到對象
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);
}
從上面三級緩存的分析,我們可以知道,Spring解決循環依賴的訣竅就在于singletonFactories這個三級cache。這個cache的類型是ObjectFactory,定義如下:
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}
這個接口在AbstractBeanFactory里實現,并在核心方法doCreateBean()引用下面的方法:
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);
}
}
}
這段代碼發生在createBeanInstance之后,populateBean()之前,也就是說單例對象此時已經被創建出來(調用了構造器)。這個對象已經被生產出來了,此時將這個對象提前曝光出來
所以在發生循環依賴時,B還未初始化,所以@PostConstruct方法還未執行
解決方案
解決循環依賴
將邏輯從@PostConstruct里抽出來