在Spring中Bean有許多不一樣的做用域,例如:singleton、prototype、request等等,本篇文章就來分析一下各個scope的Bean是怎麼建立的java
代碼:spring
// Create bean instance.
//建立單例Bean
if (mbd.isSingleton()) {
//這裏使用了一個匿名內部類,建立Bean實例對象,而且註冊給所依賴的對象
sharedInstance = getSingleton(beanName, () -> {
try {
//建立一個指定Bean實例對象,若是有父級繼承,則合併子類和父類的定義
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
//顯式地從容器單例模式Bean緩存中清除實例對象
destroySingleton(beanName);
throw ex;
}
});
//獲取給定Bean的實例對象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
複製代碼
這裏使用了匿名內部類,先經過createBean(beanName, mbd, args)
方法獲取一個 ObjectFactory緩存
把 ObjectFactory 做爲參數傳入 getSingleton(beanName,objectFactory)
方法session
使用 getSingleton(beanName,objectFactory) 方法返回的 sharedInstance 做爲參數傳入 getObjectForBeanInstance(sharedInstance, name, beanName, mbd)
來回去最終的Bean實例(詳情見Spring Ioc 之 Bean的加載(一))app
createBean(beanName, mbd, args)
方法比較複雜,在以後的文章中會詳細分析,這裏就先略過,直接看 getSingleton(beanName,objectFactory)
方法。ide
// DefaultSingletonBeanRegistry.java
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
//全局加鎖
synchronized (this.singletonObjects) {
// 從緩存中獲取單例bean
// 由於 singleton 模式其實就是複用已經建立的 bean 因此這步驟必須檢查
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//是否正在銷燬該bean
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
// 加載前置處理
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 初始化 bean
// 這個過程實際上是調用 createBean() 方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
//後置處理
afterSingletonCreation(beanName);
}
if (newSingleton) {
//加入緩存中
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
複製代碼
在這段代碼中,其實主要是作了一些準備和預處理步驟,真正建立Bean是在singletonFactory.getObject()
方法實現的,而 singletonFactory 是由createBean()方法建立後回調的參數。
那麼這段代碼主要作的事情是什麼呢?post
嘗試從緩存中獲取單例Bean
若是已經加載了則直接返回,不然開始加載過程this
加載前置處理spa
獲取Bean實例prototype
後置處理
加入緩存
beforeSingletonCreation(beanName)
是個標記方法,咱們來看代碼:
// 用於添加標誌,當前 bean 正處於建立中
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
//添加失敗,拋出異常
throw new BeanCurrentlyInCreationException(beanName);
}
}
複製代碼
把 beanName 添加到 singletonsCurrentlyInCreation
map中,用來表示該單例bean正在建立,若是添加失敗,拋出異常。
經過createBean(beanName)
方法返回的 singletonFactory 獲取Bean。
afterSingletonCreation(beanName)
一樣是個表示方法:
// 用於移除標記,當前 Bean 不處於建立中
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
//移除失敗,拋出異常
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
複製代碼
建立Bean以後移除建立標示。
前置處理和後置處理的這個建立標示,會在調用isSingletonCurrentlyInCreation(String beanName)
時用到,該方法用來判斷當前bean是否已經在建立中。
直接看代碼:
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 操做。
【put】singletonObjects 屬性,單例 bean 的緩存。
【remove】singletonFactories 屬性,單例 bean Factory 的緩存。
【remove】earlySingletonObjects 屬性,「早期」建立的單例 bean 的緩存。
【add】registeredSingletons 屬性,已經註冊的單例緩存。
代碼:
//建立多例Bean
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
//原型模式(Prototype)是每次都會建立一個新的對象
Object prototypeInstance = null;
try {
//加載前置處理,默認的功能是註冊當前建立的原型對象
beforePrototypeCreation(beanName);
//建立指定Bean對象實例
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
//加載後置處理,默認的功能告訴IOC容器指定Bean的原型對象再也不建立
afterPrototypeCreation(beanName);
}
//獲取給定Bean的實例對象
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
複製代碼
原型模式很簡單,直接建立一個新的實例就行了,再也不從緩存中去獲取。
beforePrototypeCreation(beanName)
前置處理,將當前bean標記爲正在建立的原型。
afterPrototypeCreation(beanName)
後置處理,取消當前bean的正在建立標示。
調用getObjectFrBeanInstance()
方法獲取最終bean。(詳情見Spring Ioc 之 Bean的加載(一))
//要建立的Bean既不是Singleton也不是Prototype
//如:request、session、application等生命週期
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
//Bean定義資源中沒有配置生命週期範圍,則Bean定義不合法
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的實例對象
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);
}
}
複製代碼
分爲如下幾個步驟:
從Scope註解中獲取scope名稱
前置處理
createBean()
後置處理
scope.get()獲取bean
getObjectForBeanInstance()方法獲取Bean
核心流程與原型模式同樣,只不過這裏調用了scope.get()來獲取bean。
Object get(String name, ObjectFactory<?> objectFactory);
複製代碼
scope.get()是一個接口,它有多種實現類:
咱們看一下spring自帶的一個實現 SimpleThreadScope:
//SimpleThreadScope.java
private final ThreadLocal<Map<String, Object>> threadScope =
new NamedThreadLocal<Map<String, Object>>("SimpleThreadScope") {
@Override
protected Map<String, Object> initialValue() {
return new HashMap<>();
}
};
//獲取bean的實例
@Override
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;
}
複製代碼
其餘scope的實現就不一一去看了,感興趣的朋友能夠本身看一下。
總結
上面的代碼中有2個重要方法:
createBean(beanName, mbd, args)
getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd)
這2個方法在3個代碼分支中都用到了,createBean
下篇文章會詳細分析,getObjectForBeanInstance
方法在Spring Ioc 之 Bean的加載(一)中已經分析過了。
這裏再引用下《Spring 源碼深度解析》對該方法的分析:
這個方法主要是驗證如下咱們獲得的 bean 的正確性,其實就是檢測當前 bean 是不是 FactoryBean 類型的 bean 。
若是是,那麼須要調用該 bean 對應的 FactoryBean 實例的 getObject() 方法,做爲返回值。
不管是從緩存中得到到的 bean 仍是經過不一樣的 scope 策略加載的 bean 都只是最原始的 bean 狀態,並不必定就是咱們最終想要的 bean。
舉個例子,假如咱們須要對工廠 bean 進行處理,那麼這裏獲得的實際上是工廠 bean 的初始狀態,可是咱們真正須要的是工廠 bean 中定義 factory-method 方法中返回的 bean,而 getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) 方法,就是完成這個工做的。
參考: 《Spring 源碼深度解析》- 郝佳 芋道源碼