在 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 由回調方法産生。那麼這個方法做了哪些準備呢?
- 再次檢查緩存是否已經加載過,如果已經加載了則直接傳回,否則開始加載過程。
- 調用
記錄加載單例 bean 之前的加載狀态,即前置處理。beforeSingletonCreation()
- 調用參數傳遞的 ObjectFactory 的
執行個體化 bean。getObject()
-
進行加載單例後的後置處理。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);
-
}
原型模式的初始化過程很簡單:直接建立一個新的執行個體就可以了。過程如下:
-
記錄加載原型模式 bean 之前的加載狀态,即前置處理。beforeSingletonCreation()
-
建立一個 bean 執行個體對象。createBean()
-
進行加載原型模式 bean 後的後置處理。afterSingletonCreation()
-
從 bean 執行個體中擷取對象。getObjectForBeanInstance()
其他作用域
-
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自己劃分的)已經分析完畢了。
原文釋出時間為:2018-10-24
本文作者:Java技術驿站
本文來自雲栖社群合作夥伴“
Java技術驿站”,了解相關資訊可以關注“
”。