精品專欄
從這篇部落格開始我們開始載入 bean 的第一個步驟,從快取中獲取 bean,程式碼片段如下:
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
首先呼叫 getSingleton()
從快取中獲取 bean,在上篇部落格 【死磕 Spring】—– 載入 bean 之 開啟 bean 的載入提到過,Spring 對單例樣式的 bean 只會建立一次,後續如果再獲取該 bean 則是直接從單例快取中獲取,該過程就體現在 getSingleton()
中。如下:
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 從單例緩衝中載入 bean
Object singletonObject = this.singletonObjects.get(beanName);
// 快取中的 bean 為空,且當前 bean 正在建立
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 加鎖
synchronized (this.singletonObjects) {
// 從 earlySingletonObjects 獲取
singletonObject = this.earlySingletonObjects.get(beanName);
// earlySingletonObjects 中沒有,且允許提前建立
if (singletonObject == null && allowEarlyReference) {
// 從 singletonFactories 中獲取對應的 ObjectFactory
ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);
// ObjectFactory 不為空,則建立 bean
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
這段程式碼非常簡單,首先從 singletonObjects 中獲取,若為空且當前 bean 正在建立中,則從 earlySingletonObjects 中獲取,若為空且允許提前建立則從 singletonFactories 中獲取相應的 ObjectFactory ,若不為空,則呼叫其 getObject()
建立 bean,然後將其加入到 earlySingletonObjects,然後從 singletonFactories 刪除。總體邏輯就是根據 beanName 依次檢測這三個 Map,若為空,從下一個,否則傳回。這三個 Map 存放的都有各自的功能,如下:
-
singletonObjects :存放的是單例 bean,對應關係為
bean name-->bean instance
-
earlySingletonObjects:存放的是早期的 bean,對應關係也是
bean name-->bean instance
。它與 singletonObjects 區別在於 earlySingletonObjects 中存放的 bean 不一定是完整的,從上面過程中我們可以瞭解,bean 在建立過程中就已經加入到 earlySingletonObjects 中了,所以當在 bean 的建立過程中就可以透過getBean()
方法獲取。這個 Map 也是解決迴圈依賴的關鍵所在。 -
singletonFactories:存放的是 ObjectFactory,可以理解為建立單例 bean 的 factory,對應關係是
bean name-->ObjectFactory
在上面程式碼中還有一個非常重要的檢測方法 isSingletonCurrentlyInCreation(beanName)
,該方法用於判斷該 beanName 對應的 bean 是否在建立過程中,註意這個過程講的是整個工廠中。如下:
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
從這段程式碼中我們可以預測,在 bean 建立過程中都會將其加入到 singletonsCurrentlyInCreation 集合中,具體是在什麼時候加的,我們後面分析。
到這裡從快取中獲取 bean 的過程已經分析完畢了,我們再看開篇的程式碼段,從快取中獲取 bean 後,若其不為 null 且 args 為空,則會呼叫 getObjectForBeanInstance()
處理。為什麼會有這麼一段呢?因為我們從快取中獲取的 bean 是最原始的 bean 並不一定使我們最終想要的 bean,怎麼辦呢?呼叫 getObjectForBeanInstance()
進行處理,該方法的定義為獲取給定 bean 實體的物件,該物件要麼是 bean 實體本身,要麼就是 FactoryBean 建立的物件,如下:
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// 若為工廠類取用(name 以 & 開頭)
if (BeanFactoryUtils.isFactoryDereference(name)) {
// 如果是 NullBean,則直接傳回
if (beanInstance instanceof NullBean) {
return beanInstance;
}
// 如果 beanInstance 不是 FactoryBean 型別,則丟擲異常
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}
}
// 到這裡我們就有了一個 bean 實體,當然該實體可能是會是是一個正常的 bean 又或者是一個 FactoryBean
// 如果是 FactoryBean,我我們則建立該 bean
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
// 載入 FactoryBean
Object object = null;
// 若 BeanDefinition 為 null,則從快取中載入
if (mbd == null) {
object = getCachedObjectForFactoryBean(beanName);
}
// 若 object 依然為空,則可以確認,beanInstance 一定是 FactoryBean
if (object == null) {
FactoryBean> factory = (FactoryBean>) beanInstance;
//
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
// 是否是使用者定義的而不是應用程式本身定義的
boolean synthetic = (mbd != null && mbd.isSynthetic());
// 核心處理類
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
該方法主要是進行檢測工作的,主要如下:
-
若 name 為工廠相關的(以 & 開頭),且 beanInstance 為 NullBean 型別則直接傳回,如果 beanInstance 不為 FactoryBean 型別則丟擲 BeanIsNotAFactoryException 異常。這裡主要是校驗 beanInstance 的正確性。
-
如果 beanInstance 不為 FactoryBean 型別或者 name 也不是與工廠相關的,則直接傳回。這裡主要是對非 FactoryBean 型別處理。
-
如果 BeanDefinition 為空,則從 factoryBeanObjectCache 中載入,如果還是空,則可以斷定 beanInstance 一定是 FactoryBean 型別,則委託
getObjectFromFactoryBean()
方法處理
從上面可以看出 getObjectForBeanInstance()
主要是傳回給定的 bean 實體物件,當然該實體物件為非 FactoryBean 型別,對於 FactoryBean 型別的 bean,則是委託 getObjectFromFactoryBean()
從 FactoryBean 獲取 bean 實體物件。
protected Object getObjectFromFactoryBean(FactoryBean> factory, String beanName, boolean shouldPostProcess) {
// 為單例樣式且快取中存在
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
// 從快取中獲取指定的 factoryBean
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// 為空,則從 FactoryBean 中獲取物件
object = doGetObjectFromFactoryBean(factory, beanName);
// 從快取中獲取
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
// **我實在是不明白這裡這麼做的原因,這裡是幹嘛???**
if (alreadyThere != null) {
object = alreadyThere;
}
else {
// 需要後續處理
if (shouldPostProcess) {
// 若該 bean 處於建立中,則傳回非處理物件,而不是儲存它
if (isSingletonCurrentlyInCreation(beanName)) {
return object;
}
// 前置處理
beforeSingletonCreation(beanName);
try {
// 對從 FactoryBean 獲取的物件進行後處理
// 生成的物件將暴露給bean取用
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
}
finally {
// 後置處理
afterSingletonCreation(beanName);
}
}
// 快取
if (containsSingleton(beanName)) {
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
}
else {
// 非單例
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
return object;
}
}
主要流程如下:
-
若為單例且單例 bean 快取中存在 beanName,則進行後續處理(跳轉到下一步),否則則從 FactoryBean 中獲取 bean 實體物件,如果接受後置處理,則呼叫
postProcessObjectFromFactoryBean()
進行後置處理。 -
首先獲取鎖(其實我們在前面篇幅中發現了大量的同步鎖,鎖住的物件都是 this.singletonObjects, 主要是因為在單例樣式中必須要保證全域性唯一),然後從 factoryBeanObjectCache 快取中獲取實體物件 object,若 object 為空,則呼叫
doGetObjectFromFactoryBean()
方法從 FactoryBean 獲取物件,其實內部就是呼叫FactoryBean.getObject()
。 -
如果需要後續處理,則進行進一步處理,步驟如下:
-
若該 bean 處於建立中(isSingletonCurrentlyInCreation),則傳回非處理物件,而不是儲存它
-
呼叫
beforeSingletonCreation()
進行建立之前的處理。預設實現將該 bean 標誌為當前建立的。 -
呼叫
postProcessObjectFromFactoryBean()
對從 FactoryBean 獲取的 bean 實體物件進行後置處理,預設實現是按照原樣直接傳回,具體實現是在 AbstractAutowireCapableBeanFactory 中實現的,當然子類也可以重寫它,比如應用後置處理 -
呼叫
afterSingletonCreation()
進行建立 bean 之後的處理,預設實現是將該 bean 標記為不再在建立中。 -
最後加入到 FactoryBeans 快取中。
該方法應該就是建立 bean 實體物件中的核心方法之一了。這裡我們關註三個方法: beforeSingletonCreation()
、 afterSingletonCreation()
、 postProcessObjectFromFactoryBean()
。可能有小夥伴覺得前面兩個方法不是很重要,LZ 可以肯定告訴你,這兩方法是非常重要的操作,因為他們記錄著 bean 的載入狀態,是檢測當前 bean 是否處於建立中的關鍵之處,對解決 bean 迴圈依賴起著關鍵作用。before 方法用於標誌當前 bean 處於建立中,after 則是移除。其實在這篇部落格剛剛開始就已經提到了 isSingletonCurrentlyInCreation()
是用於檢測當前 bean 是否處於建立之中,如下:
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
是根據 singletonsCurrentlyInCreation 集合中是否包含了 beanName,集合的元素則一定是在 beforeSingletonCreation()
中新增的,如下:
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
afterSingletonCreation()
為移除,則一定就是對 singletonsCurrentlyInCreation 集合 remove 了,如下:
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
postProcessObjectFromFactoryBean()
是對從 FactoryBean 處獲取的 bean 實體物件進行後置處理,其預設實現是直接傳回 object 物件,不做任何處理,子類可以重寫,例如應用後處理器。AbstractAutowireCapableBeanFactory 對其提供了實現,如下:
protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {
return applyBeanPostProcessorsAfterInitialization(object, beanName);
}
該方法的定義為:對所有的 {@code postProcessAfterInitialization} 進行回呼註冊 BeanPostProcessors,讓他們能夠後期處理從 FactoryBean 中獲取的物件。下麵是具體實現:
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
對於後置處理器,這裡我們不做過多闡述,後面會專門的博文進行詳細介紹,這裡我們只需要記住一點:盡可能保證所有 bean 初始化後都會呼叫註冊的 BeanPostProcessor.postProcessAfterInitialization()
方法進行處理,在實際開發過程中大可以針對此特性設計自己的業務邏輯。
至此,從快取中獲取 bean 物件過程已經分析完畢了。
下麵兩篇部落格分析,如果從單例快取中沒有獲取到單例 bean,則 Spring 是如何處理的?
【死磕 Spring】----- IOC 之 IOC 初始化總結
【死磕 Spring】----- IOC 之解析 bean 標簽:解析自定義標簽
【死磕 Spring】----- IOC 之解析 bean 標簽:constructor-arg、property 子元素
【死磕 Spring】—– IOC 之解析 bean 標簽:meta、lookup-method、replace-method)
【死磕 Spring】----- IOC 之解析 bean 標簽:開啟解析行程
【死磕 Spring】----- IOC 之 獲取 Document 物件
【死磕 Spring】----- IOC 之 註冊 BeanDefinition
【死磕 Spring】----- IOC 之 Spring 統一資源載入策略
【死磕 Spring】----- IOC 之深入理解 Spring IoC
END
我是 Java 技術驛站,感謝有你
>>>>>> 加群交流技術 <<<<<<