精品專欄
在 Spring 中存在著不同的 scope,預設是 singleton ,還有 prototype、request 等等其他的 scope,他們的初始化步驟是怎樣的呢?這個答案在這篇部落格中給出。
singleton
Spring 的 scope 預設為 singleton,其初始化的程式碼如下:
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
第一部分分析了從快取中獲取單例樣式的 bean,但是如果快取中不存在呢?則需要從頭開始載入 bean,這個過程由 getSingleton()
實現。
public Object getSingleton(String beanName, ObjectFactory> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// 全域性加鎖
synchronized (this.singletonObjects) {
// 從快取中檢查一遍
// 因為 singleton 樣式其實就是復用已經建立的 bean 所以這步驟必須檢查
Object singletonObject = this.singletonObjects.get(beanName);
// 為空,開始載入過程
if (singletonObject == null) {
// 省略 部分程式碼
// 載入前置處理
beforeSingletonCreation(beanName);
boolean newSingleton = false;
// 省略程式碼
try {
// 初始化 bean
// 這個過程其實是呼叫 createBean() 方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
// 省略 catch 部分
}
finally {
// 後置處理
afterSingletonCreation(beanName);
}
// 加入快取中
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
// 直接傳回
return singletonObject;
}
}
其實這個過程並沒有真正建立 bean,僅僅只是做了一部分準備和預處理步驟,真正獲取單例 bean 的方法其實是由 singletonFactory.getObject()
這部分實現,而 singletonFactory 由回呼方法產生。那麼這個方法做了哪些準備呢?
-
再次檢查快取是否已經載入過,如果已經載入了則直接傳回,否則開始載入過程。
-
呼叫
beforeSingletonCreation()
記錄載入單例 bean 之前的載入狀態,即前置處理。 -
呼叫引數傳遞的 ObjectFactory 的
getObject()
實體化 bean。 -
呼叫
afterSingletonCreation()
進行載入單例後的後置處理。 -
將結果記錄並加入值快取中,同時刪除載入 bean 過程中所記錄的一些輔助狀態。
流程中涉及的三個方法 beforeSingletonCreation()
與 afterSingletonCreation()
在部落格 【死磕 Spring】----- 載入 bean 之 快取中獲取單例 bean 中分析過了,所以這裡不再闡述了,我們看另外一個方法 addSingleton()
。
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
一個 put、一個 add、兩個 remove。singletonObjects 單例 bean 的快取,singletonFactories 單例 bean Factory 的快取,earlySingletonObjects “早期”建立的單例 bean 的快取,registeredSingletons 已經註冊的單例快取。
載入了單例 bean 後,呼叫 getObjectForBeanInstance()
從 bean 實體中獲取物件。該方法已經在 【死磕 Spring】----- 載入 bean 之 快取中獲取單例 bean 詳細分析了。
原型樣式
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
原型樣式的初始化過程很簡單:直接建立一個新的實體就可以了。過程如下:
-
呼叫
beforeSingletonCreation()
記錄載入原型樣式 bean 之前的載入狀態,即前置處理。 -
呼叫
createBean()
建立一個 bean 實體物件。 -
呼叫
afterSingletonCreation()
進行載入原型樣式 bean 後的後置處理。 -
呼叫
getObjectForBeanInstance()
從 bean 實體中獲取物件。
其他作用域
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
核心流程和原型樣式一樣,只不過獲取 bean 實體是由 scope.get()
實現,如下:
public Object get(String name, ObjectFactory> objectFactory) {
// 獲取 scope 快取
Map<String, Object> scope = this.threadScope.get();
Object scopedObject = scope.get(name);
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
// 加入快取
scope.put(name, scopedObject);
}
return scopedObject;
}
對於上面三個模組,其中最重要的有兩個方法,一個是 createBean()
、一個是 getObjectForBeanInstance()
。這兩個方法在上面三個模組都有呼叫, createBean()
後續詳細說明, getObjectForBeanInstance()
在部落格 【死磕 Spring】----- 載入 bean 之 快取中獲取單例 bean 中有詳細講解,這裡再次闡述下(此段內容來自《Spring 原始碼深度解析》):這個方法主要是驗證以下我們得到的 bean 的正確性,其實就是檢測當前 bean 是否是 FactoryBean 型別的 bean,如果是,那麼需要呼叫該 bean 對應的 FactoryBean 實體的 getObject()
作為傳回值。無論是從快取中獲得到的 bean 還是透過不同的 scope 策略載入的 bean 都只是最原始的 bean 狀態,並不一定就是我們最終想要的 bean。舉個例子,加入我們需要對工廠 bean 進行處理,那麼這裡得到的其實是工廠 bean 的初始狀態,但是我們真正需要的是工廠 bean 中定義 factory-method 方法中傳回的 bean,而 getObjectForBeanInstance()
就是完成這個工作的。
至此,Spring 載入 bean 的三個部分(LZ自己劃分的)已經分析完畢了。
【死磕 Spring】----- IOC 之開啟 bean 的載入
【死磕 Spring】----- IOC 之從單例快取中獲取單例 bean
【死磕 Spring】----- IOC 之parentBeanFactory 與依賴處理
【死磕 Spring】----- IOC 之 IOC 初始化總結
END
>>>>>> 加群交流技術 <<<<<<