該系列文章是本人在學習 Spring 的過程中總結下來的,裡面涉及到相關原始碼,可能對讀者不太友好,請結合我的原始碼註釋 Spring 原始碼分析 GitHub 地址 進行閱讀
Spring 版本:5.1.14.RELEASE
開始閱讀這一系列文章之前,建議先檢視《深入瞭解 Spring IoC(面試題)》這一篇文章
該系列其他文章請檢視:《死磕 Spring 之 IoC 篇 - 文章導讀》
開啟 Bean 的載入
前面的一些列文章對面向資源(XML、Properties)、面向註解定義的 Bean 是如何被解析成 BeanDefinition(Bean 的“前身”),並儲存至 BeanDefinitionRegistry 註冊中心裡面,實際也是通過 ConcurrentHashMap 進行儲存。
Spring 底層 IoC 容器 DefaultListableBeanFactory,實現了 BeanFactory 和 BeanDefinitionRegistry 介面,這個時候它處於“就緒狀態”,當我們顯示或者隱式地呼叫 getBean(...)
方法時,會觸發載入 Bean 階段,獲取對應的 Bean。在該方法中,如果是單例模式會先從快取中獲取,已有則直接返回,沒有則根據 BeanDefinition 開始初始化這個 Bean。
BeanFactory 體系結構
先來看看 BeanFactory 介面的繼承關係
簡單描述這些介面:
-
org.springframework.beans.factory.BeanFactory
,Spring IoC 容器最基礎的介面,提供依賴查詢單個 Bean 的功能 -
org.springframework.beans.factory.ListableBeanFactory
,繼承 BeanFactory 介面,提供依賴查詢多個 Bean 的功能 -
org.springframework.beans.factory.HierarchicalBeanFactory
,繼承 BeanFactory 介面,提供獲取父 BeanFactory 的功能,具有層次性 -
org.springframework.beans.factory.config.ConfigurableBeanFactory
,繼承 HierarchicalBeanFactory 介面,提供可操作內部相關元件的功能,具有可配置性 -
org.springframework.beans.factory.config.AutowireCapableBeanFactory
,繼承 BeanFactory 介面,提供可注入的功能,支援依賴注入 -
org.springframework.beans.factory.config.ConfigurableListableBeanFactory
,繼承上面所有介面,綜合所有特性,還提供可提前初始化所有單例 Bean 的功能
通過這些介面的名稱可以大致瞭解其用意,接下來我們來看看它們的實現類的繼承關係
簡單描述這些實現類:
org.springframework.beans.factory.support.AbstractBeanFactory
抽象類,實現 ConfigurableBeanFactory 介面,基礎實現類,Bean 的建立過程交由子類實現org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
抽象類,繼承 AbstractBeanFactory,實現 AutowireCapableBeanFactory 介面,完成 Bean 的建立org.springframework.beans.factory.support.DefaultListableBeanFactory
,Spring 底層 IoC 容器,依賴注入的底層實現
其他的介面和類和 BeanDefinition 註冊中心,別名註冊中心,單例 Bean 註冊中心相關;右下角的 ApplicationContext 與 Spring 應用上下文有關,它的整個體系這裡不做展述,在後面的文章進行分析
AbstractBeanFactory
org.springframework.beans.factory.support.AbstractBeanFactory
抽象類,實現 ConfigurableBeanFactory 介面,BeanFactory 的基礎實現類,提供依賴查詢方法,可獲取 Bean 物件,接下來我們來看看依賴查詢的實現
getBean 方法
getBean(String name)
方法,根據名稱獲取 Bean,當然還有許多過載方法,如下:
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return doGetBean(name, requiredType, null, false);
}
@Override
public Object getBean(String name, Object... args) throws BeansException {
return doGetBean(name, null, args, false);
}
public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
throws BeansException {
return doGetBean(name, requiredType, args, false);
}
最終都會呼叫 doGetBean(...)
這個方法
當我們顯示或者隱式地呼叫這個方法時,會觸發 Bean 的載入;你是否會有疑問,我們使用 Spring 的過程中並不會呼叫這個方法去獲取 Bean,那這個方法會被誰呼叫呢?在 ConfigurableListableBeanFactory 介面中提供提前初始化所有單例 Bean 的功能,在 Spring 應用上下文(ApplicationContext)重新整理階段會提前初始化所有的單例 Bean,這個提前初始化也是呼叫 getBean 這個方法,這部分內容在後續分析 Spring 應用上下文的生命週期會講到
【核心】doGetBean 方法
doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly)
方法,獲取一個 Bean,方法如下:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// <1> 獲取 `beanName`
// 因為入參 `name` 可能是別名,也可能是 FactoryBean 型別 Bean 的名稱(`&` 開頭,需要去除)
// 所以需要獲取真實的 beanName
final String beanName = transformedBeanName(name);
Object bean;
// <2> 先從快取(僅快取單例 Bean )中獲取 Bean 物件,這裡快取指的是 `3` 個 Map
// 快取中也可能是正在初始化的 Bean,可以避免**迴圈依賴注入**引起的問題
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
// <3> 若從快取中獲取到對應的 Bean,且 `args` 引數為空
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// <3.1> 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接返回
// 否則,呼叫 FactoryBean#getObject() 獲取目標物件
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// 快取中沒有對應的 Bean,則開啟 Bean 的載入
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
// <4> 如果**非單例模式**下的 Bean 正在建立,這裡又開始建立,表明存在迴圈依賴,則直接丟擲異常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
// <5> 如果從當前容器中沒有找到對應的 BeanDefinition,則從父容器中載入(如果存在父容器)
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
// <5.1> 獲取 `beanName`,因為可能是別名,則進行處理
// 和第 `1` 步不同,不需要對 `&` 進行處理,因為進入父容器重新依賴查詢
String nameToLookup = originalBeanName(name);
// <5.2> 若為 AbstractBeanFactory 型別,委託父容器的 doGetBean 方法進行處理
// 否則,就是非 Spring IoC 容器,根據引數呼叫相應的 `getBean(...)`方法
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
// <6> 如果不是僅僅做型別檢查,則表示需要建立 Bean,將 `beanName` 標記為已建立過
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
// <7> 從容器中獲取 `beanName` 對應的的 RootBeanDefinition(合併後)
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 檢查是否為抽象類
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
// <8> 獲取當前正在建立的 Bean 所依賴物件集合(`depends-on` 配置的依賴)
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
// <8.1> 檢測是否存在迴圈依賴,存在則丟擲異常
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// <8.2> 將 `beanName` 與 `dep` 之間依賴的關係進行快取
registerDependentBean(dep, beanName);
try {
// <8.3> 先建立好依賴的 Bean(重新呼叫 `getBean(...)` 方法)
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// Create bean instance.
// <9> 開始建立 Bean,不同模式建立方式不同
if (mbd.isSingleton()) { // <9.1> 單例模式
/*
* <9.1.1> 建立 Bean,成功建立則進行快取,並移除快取的早期物件
* 建立過程實際呼叫的下面這個 `createBean(...)` 方法
*/
sharedInstance = getSingleton(beanName,
// ObjectFactory 實現類
() -> {
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;
}
});
// <9.1.2> 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接返回
// 否則,呼叫 FactoryBean#getObject() 獲取目標物件
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// <9.2> 原型模式
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
// <9.2.1> 將 `beanName` 標記為原型模式正在建立
beforePrototypeCreation(beanName);
// <9.2.2> **【核心】** 建立 Bean
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
// <9.2.3> 將 `beanName` 標記為不在建立中,照應第 `9.2.1` 步
afterPrototypeCreation(beanName);
}
// <9.2.4> 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接返回
// 否則,呼叫 FactoryBean#getObject() 獲取目標物件
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// <9.3> 其他模式
else {
// <9.3.1> 獲取該模式的 Scope 物件 `scope`,不存在則丟擲異常
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 {
// <9.3.1> 從 `scope` 中獲取 `beanName` 對應的物件(看你的具體實現),不存在則執行**原型模式**的四個步驟進行建立
Object scopedInstance = scope.get(beanName, () -> {
// 將 `beanName` 標記為原型模式正在建立
beforePrototypeCreation(beanName);
try {
// **【核心】** 建立 Bean
return createBean(beanName, mbd, args);
}
finally {
// 將 `beanName` 標記為不在建立中,照應上一步
afterPrototypeCreation(beanName);
}
});
// 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接返回
// 否則,呼叫 FactoryBean#getObject() 獲取目標物件
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);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// Check if required type matches the type of the actual bean instance.
// <10> 如果入參 `requiredType` 不為空,並且 Bean 不是該型別,則需要進行型別轉換
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
// <10.1> 通過型別轉換機制,將 Bean 轉換成 `requiredType` 型別
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
// <10.2> 轉換後的 Bean 為空則丟擲異常
if (convertedBean == null) {
// 轉換失敗,丟擲 BeanNotOfRequiredTypeException 異常
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
// <10.3> 返回型別轉換後的 Bean 物件
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
// <11> 返回獲取到的 Bean
return (T) bean;
}
這個方法的處理過程有點長,如下:
-
獲取
beanName
,因為入參name
可能是別名,也可能是 FactoryBean 型別 Bean 的名稱(&
開頭,需要去除),所以需要獲取真實的beanName
-
先從快取(僅快取單例 Bean )中獲取 Bean 物件,這裡快取指的是
3
個 Map;快取中也可能是正在初始化的 Bean,可以避免迴圈依賴注入引起的問題 -
若從快取中獲取到對應的 Bean,且
args
引數為空-
【同】呼叫
getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd)
方法獲取 Bean 的目標物件,
scopedInstance
非 FactoryBean 型別直接返回,否則,呼叫 FactoryBean#getObject() 獲取目標物件
-
快取中沒有對應的 Bean,則開啟 Bean 的載入
-
如果非單例模式下的 Bean 正在建立,這裡又開始建立,表明存在迴圈依賴,則直接丟擲異常
-
如果從當前容器中沒有找到對應的 BeanDefinition,則從父容器中載入(如果存在父容器)
- 獲取
beanName
,因為可能是別名,則進行處理,和第1
步不同,不需要對&
進行處理,因為進入父容器重新依賴查詢 - 若為 AbstractBeanFactory 型別,委託父容器的 doGetBean 方法進行處理;否則,就是非 Spring IoC 容器,根據引數呼叫相應的
getBean(...)
方法
- 獲取
-
如果不是僅僅做型別檢查,則表示需要建立 Bean,將
beanName
標記為已建立過,在後面的迴圈依賴檢查中會使用到 -
從容器中獲取
beanName
對應的的 RootBeanDefinition(合併後),呼叫getMergedLocalBeanDefinition(String beanName)
方法 -
獲取當前正在建立的 Bean 所依賴物件集合(
depends-on
配置的依賴)- 檢測是否存在迴圈依賴,存在則丟擲異常
- 將
beanName
與dep
之間依賴的關係進行快取 - 先建立好依賴的 Bean(重新呼叫
getBean(...)
方法)
-
開始建立 Bean,不同模式建立方式不同
-
單例模式
-
建立 Bean,成功建立則進行快取,並移除快取的早期物件,呼叫
getSingleton(String beanName, ObjectFactory<?> singletonFactory)
方法【核心】入參的 ObjectFactory 實現類就是呼叫的
AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[])
方法 -
【同】 和上面的
3.1
相同操作
-
-
原型模式
- 將
beanName
標記為非單例模式正在建立 - 【核心】 建立 Bean,呼叫
AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[])
方法 - 將
beanName
標記為不在建立中,照應第9.2.1
步 - 【同】 和上面的
3.1
相同操作
- 將
-
其他模式
- 獲取該模式的 Scope 物件
scope
,不存在則丟擲異常 - 從
scope
中獲取beanName
對應的物件(看你的具體實現),不存在則執行原型模式的四個步驟進行建立
- 獲取該模式的 Scope 物件
-
-
如果入參
requiredType
不為空,並且 Bean 不是該型別,則需要進行型別轉換- 通過型別轉換機制,將 Bean 轉換成
requiredType
型別 - 轉換後的 Bean 為空則丟擲異常
- 返回型別轉換後的 Bean 物件
- 通過型別轉換機制,將 Bean 轉換成
-
返回獲取到的 Bean
概括:
-
可以看到這個方法載入 Bean 的過程中,會先從快取中獲取單例模式的 Bean;
-
不管是從快取中獲取的還是新建立的,都會進行處理,如果是 FactoryBean 型別則呼叫其 getObject() 獲取目標物件;
-
BeanFactory 可能有父容器,如果當前容器找不到 BeanDefinition 則會嘗試讓父容器建立;
-
建立 Bean 的任務交由 AbstractAutowireCapableBeanFactory 去完成;
-
如果獲取到的 Bean 不是我們想要型別,會通過型別轉換機制轉換成目標型別
接下來依次分析上述過程的相關步驟(doGetBean(...)
)
1. 獲取 beanName
對應程式碼段:
// AbstractBeanFactory.java
final String beanName = transformedBeanName(name);
因為入參 name
可能是別名,也可能是 FactoryBean 型別 Bean 的名稱(&
開頭,需要去除),所以需要進行一番轉換,如下:
// AbstractBeanFactory.java
protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
// BeanFactoryUtils.java
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
return name;
}
// 獲取 name 對應的 beanName,
// 不為 null 則返回 `transformedBeanNameCache` 快取中對應的 beanName,
// 為 null 則對 name 進行處理,將字首 '&' 去除,直至沒有 '&',然後放入 `transformedBeanNameCache` 快取中,並返回處理後的 beanName
return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
do {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
return beanName;
});
}
// SimpleAliasRegistry.java
public String canonicalName(String name) {
String canonicalName = name;
// Handle aliasing...
String resolvedName;
// 迴圈,從 aliasMap 中,獲取到最終的 beanName
do {
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}
過程並不複雜,先將字首 &
去除(如果存在),如果是別名則獲取對應的 beanName
定義了一個 FactoryBean 型別的 Bean,名稱為
user
,通過user
獲取 Bean,獲取到的是 FactoryBean#getObject() 返回的物件(只會被呼叫一次)通過
&user
獲取 Bean,獲取到的是 FactoryBean 本身這個物件
2. 從快取中獲取單例 Bean
對應程式碼段:
// AbstractBeanFactory#doGetBean(...) 方法
Object sharedInstance = getSingleton(beanName);
單例模式的 Bean 被建立後會快取,為了避免迴圈依賴注入,在建立過程會臨時快取正在建立的 Bean(早期 Bean),在後續文章會講到,從快取中獲取物件過程如下:
// DefaultSingletonBeanRegistry.java
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// <1> **【一級 Map】**從單例快取 `singletonObjects` 中獲取 beanName 對應的 Bean
Object singletonObject = this.singletonObjects.get(beanName);
// <2> 如果**一級 Map**中不存在,且當前 beanName 正在建立
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// <2.1> 對 `singletonObjects` 加鎖
synchronized (this.singletonObjects) {
// <2.2> **【二級 Map】**從 `earlySingletonObjects` 集合中獲取,裡面會儲存從 **三級 Map** 獲取到的正在初始化的 Bean
singletonObject = this.earlySingletonObjects.get(beanName);
// <2.3> 如果**二級 Map** 中不存在,且允許提前建立
if (singletonObject == null && allowEarlyReference) {
// <2.3.1> **【三級 Map】**從 `singletonFactories` 中獲取對應的 ObjectFactory 實現類
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
// 如果從**三級 Map** 中存在對應的物件,則進行下面的處理
if (singletonFactory != null) {
// <2.3.2> 呼叫 ObjectFactory#getOject() 方法,獲取目標 Bean 物件(早期半成品)
singletonObject = singletonFactory.getObject();
// <2.3.3> 將目標物件放入**二級 Map**
this.earlySingletonObjects.put(beanName, singletonObject);
// <2.3.4> 從**三級 Map**移除 `beanName`
this.singletonFactories.remove(beanName);
}
}
}
}
// <3> 返回從快取中獲取的物件
return singletonObject;
}
過程如下:
- 【一級 Map】從單例快取
singletonObjects
中獲取 beanName 對應的 Bean - 如果一級 Map中不存在,且當前 beanName 正在建立
- 對
singletonObjects
加鎖 - 【二級 Map】從
earlySingletonObjects
集合中獲取,裡面會儲存從 三級 Map 獲取到的正在初始化的 Bean - 如果二級 Map 中不存在,且允許提前建立
- 【三級 Map】從
singletonFactories
中獲取對應的 ObjectFactory 實現類,如果從三級 Map 中存在對應的物件,則進行下面的處理 - 呼叫 ObjectFactory#getOject() 方法,獲取目標 Bean 物件(早期半成品)
- 將目標物件放入二級 Map
- 從三級 Map移除 beanName
- 【三級 Map】從
- 對
- 返回從快取中獲取的物件
這個過程對應《深入瞭解 Spring IoC(面試題)》中的BeanFactory 是如何處理迴圈依賴問題
3. FactoryBean 的處理
一般情況下,Spring 通過反射機制利用 Bean 的 beanClass 屬性指定實現類來例項化 Bean。某些情況下,Bean 的例項化過程比較複雜,如果按照傳統的方式,則需要提供大量的配置資訊,配置方式的靈活性有限,這時採用編碼的方式可能會得到一個簡單的方案。Spring 為此提供了一個 FactoryBean 的工廠 Bean 介面,使用者可以通過實現該介面定製例項化 Bean 的邏輯。
FactoryBean 介面對於 Spring 框架本身也非常重要,其內部就提供了大量 FactoryBean 的實現。它們隱藏了例項化過程中一些複雜細節,給上層應用帶來了便利。
對應程式碼段:
// AbstractBeanFactory#doGetBean(...) 方法
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
不管是從快取中獲取的還是新建立的,都會呼叫這個方法進行處理,如果是 FactoryBean 型別則呼叫其 getObject() 獲取目標物件
getObjectForBeanInstance 方法
// AbstractBeanFactory.java
protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName,
@Nullable RootBeanDefinition mbd) {
// Don't let calling code try to dereference the factory if the bean isn't a factory.
// <1> 若 `name` 以 `&` 開頭,說明想要獲取 FactoryBean,則校驗其**正確性**
if (BeanFactoryUtils.isFactoryDereference(name)) {
// <1.1> 如果是 NullBean 空物件,則直接返回
if (beanInstance instanceof NullBean) {
return beanInstance;
}
// <1.2> 如果不是 FactoryBean 型別,則丟擲異常
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
}
// Now we have the bean instance, which may be a normal bean or a FactoryBean.
// If it's a FactoryBean, we use it to create a bean instance, unless the caller actually wants a reference to the factory.
// 到這裡我們就有了一個 Bean,可能是一個正常的 Bean,也可能是一個 FactoryBean
// 如果是 FactoryBean,則需要通過其 getObject() 方法獲取目標物件
// <2> 如果 `beanInstance` 不是 FactoryBean 型別,不需要再處理則直接返回
// 或者(表示是 FactoryBean 型別) `name` 以 `&` 開頭,表示你想要獲取實際 FactoryBean 物件,則直接返回
// 還不符合條件的話,表示是 FactoryBean,需要獲取 getObject() 返回目標物件
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
Object object = null;
// <3> 如果入參沒有傳 BeanDefinition,則從 `factoryBeanObjectCache` 快取中獲取對應的 Bean 物件
// 入參傳了 BeanDefinition 表示這個 Bean 是剛建立的,不走快取,需要呼叫其 getObject() 方法獲取目標物件
// `factoryBeanObjectCache`:FactoryBean#getObject() 呼叫一次後返回的目標物件快取在這裡
if (mbd == null) {
object = getCachedObjectForFactoryBean(beanName);
}
// <4> 若第 `3` 步獲取的物件為空,則需要呼叫 FactoryBean#getObject() 獲得物件
if (object == null) {
// Return bean instance from factory.
// <4.1> 將 `beanInstance` 轉換成 FactoryBean 型別
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
// <4.2> 如果入參沒有傳 BeanDefinition 並且當前容器存在對應的 BeanDefinition
if (mbd == null && containsBeanDefinition(beanName)) {
// 獲取對應的 RootBeanDefinition(合併後)
mbd = getMergedLocalBeanDefinition(beanName);
}
// 是否是使用者定義的(不是 Spring 建立解析出來的)
boolean synthetic = (mbd != null && mbd.isSynthetic());
// <4.3> **【核心】**通過 FactoryBean 獲得目標物件,單例模式會快取在 `factoryBeanObjectCache` 中
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
過程如下:
-
若
name
以&
開頭,說明想要獲取 FactoryBean,則校驗其正確性- 如果是 NullBean 空物件,則直接返回
- 如果不是 FactoryBean 型別,則丟擲異常
-
如果
beanInstance
不是 FactoryBean 型別,不需要再處理則直接返回;或者(表示是 FactoryBean 型別)name
以&
開頭,表示你想要獲取實際 FactoryBean 物件,則直接返回;還不符合條件的話,表示是 FactoryBean,需要獲取 getObject() 返回目標物件,往下處理 -
如果入參沒有傳 BeanDefinition,則從
factoryBeanObjectCache
快取中獲取對應的 Bean 物件,如下:// FactoryBeanRegistrySupport.java protected Object getCachedObjectForFactoryBean(String beanName) { return this.factoryBeanObjectCache.get(beanName); }
factoryBeanObjectCache
:FactoryBean#getObject() 呼叫一次後返回的目標物件快取在這裡入參傳了 BeanDefinition 表示這個 Bean 是剛建立的,不走快取,需要呼叫其 getObject() 方法獲取目標物件
-
若第
3
步獲取的物件為空,則需要呼叫 FactoryBean#getObject() 獲得物件- 將
beanInstance
轉換成 FactoryBean 型別 - 如果入參沒有傳 BeanDefinition 並且當前容器存在對應的 BeanDefinition,則獲取對應的 RootBeanDefinition(合併後)
- 【核心】通過 FactoryBean 獲得目標物件,單例模式會快取在
factoryBeanObjectCache
中,呼叫getObjectFromFactoryBean(FactoryBean<?>, String, boolean)
方法
- 將
getObjectFromFactoryBean 方法
getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess)
方法,獲取 FactoryBean 的目標物件,方法如下:
// FactoryBeanRegistrySupport.java
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
// <1> `factory` 為單例模式,且單例 Bean 快取中存在 `beanName` 對應的 FactoryBean 物件
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) { // <1.1> 獲取單例鎖,保證安全
// <1.2> 從 `factoryBeanObjectCache` 快取中獲取 FactoryBean#getObject() 建立的目標物件
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// <1.3> 則根據 `factory` 獲取目標物件,呼叫 FactoryBean#getObject() 方法
object = doGetObjectFromFactoryBean(factory, beanName);
// Only post-process and store if not put there already during getObject() call above
// (e.g. because of circular reference processing triggered by custom getBean calls)
// <1.4> 這裡再進行一次校驗,看是否在快取中存在 FactoryBean 建立的目標物件,如果有則優先從快取中獲取
// 保證 FactoryBean#getObject() 只能被呼叫一次
// 沒有的話,則對剛獲取到的目標物件進行接下來的處理
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
} else {
// <1.5> 是否需要後續處理,這個 FactoryBean 的前身 BeanDefinition 是否由 Spring 解析出來的,通常情況下都是
if (shouldPostProcess) {
// <1.5.1> 若該 FactoryBean 處於建立中,則直接返回這個目標物件,不進行接下來的處理過程
if (isSingletonCurrentlyInCreation(beanName)) {
// Temporarily return non-post-processed object, not storing it yet..
return object;
}
// <1.5.2> 前置處理,將 `beanName` 標誌為正在建立
beforeSingletonCreation(beanName);
try {
// <1.5.3> 對通過 FactoryBean 獲取的目標物件進行後置處理
// 遍歷所有的 BeanPostProcessor 的 postProcessAfterInitialization 方法(初始化的處理)
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
}
finally {
// <1.5.4> 後置處理,將 `beanName` 標誌為不在建立中
afterSingletonCreation(beanName);
}
}
// <1.6> 如果快取中存在 `beanName` 對應的 FactoryBean 物件
// 上面不是判斷了嗎?也可能在上面的處理過程會有所變化,所以這裡在做一層判斷
// 目的:快取 FactoryBean 建立的目標物件,則需要保證 FactoryBean 本身這個物件存在快取中
if (containsSingleton(beanName)) {
// <1.6.1> 將這個 FactoryBean 建立的目標物件儲存至 `factoryBeanObjectCache`
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
// <1.7> 返回 FactoryBean 建立的目標物件
return object;
}
}
// <2> `factory` 非單例模式,或單例 Bean 快取中不存在 `beanName` 對應的 FactoryBean 物件
else {
// <2.1> 則根據 `factory` 獲取目標物件,呼叫 FactoryBean#getObject() 方法
Object object = doGetObjectFromFactoryBean(factory, beanName);
// <2.2> 是否需要後續處理,這個 FactoryBean 的前身 BeanDefinition 是否由 Spring 解析出來的,通常情況下都是
if (shouldPostProcess) {
try {
// <2.2.1> 對通過 FactoryBean 獲取的目標物件進行後置處理
// 遍歷所有的 BeanPostProcessor 的 postProcessAfterInitialization 方法(初始化的處理)
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
// <2.3> 返回 FactoryBean 建立的目標物件,非單例模式不會進行快取
return object;
}
}
過程如下:
-
factory
為單例模式,且單例 Bean 快取中存在beanName
對應的 FactoryBean 物件-
獲取單例鎖,保證安全
-
從
factoryBeanObjectCache
快取中獲取 FactoryBean#getObject() 建立的目標物件 -
則根據
factory
獲取目標物件,呼叫 FactoryBean#getObject() 方法(反射機制) -
這裡再進行一次校驗(第
1.2
已經判斷過),看是否在快取中存在 FactoryBean 建立的目標物件,如果有則優先從快取中獲取,保證 FactoryBean#getObject() 只能被呼叫一次;沒有的話,則對剛獲取到的目標物件進行接下來的處理 -
是否需要後續處理,這個 FactoryBean 的 BeanDefinition 是否由 Spring 解析出來的,通常情況下都是
-
若該 FactoryBean 處於建立中,則直接返回這個目標物件,不進行接下來的處理過程
-
前置處理,將
beanName
標誌為正在建立 -
對通過 FactoryBean 獲取的目標物件進行後置處理,遍歷所有的 BeanPostProcessor 的 postProcessAfterInitialization 方法(初始化的處理)
-
後置處理,將
beanName
標誌為不在建立中 -
如果快取中存在
beanName
對應的 FactoryBean 物件,上面不是判斷了嗎(第1
步判斷過)?也可能在上面的處理過程會有所變化,所以這裡在做一層判斷,目的:快取 FactoryBean 建立的目標物件,則需要保證 FactoryBean 本身這個物件存在快取中
- 將這個 FactoryBean 建立的目標物件儲存至
factoryBeanObjectCache
- 將這個 FactoryBean 建立的目標物件儲存至
-
返回 FactoryBean 建立的目標物件
-
-
-
factory
非單例模式,或單例 Bean 快取中不存在beanName
對應的 FactoryBean 物件- 則根據
factory
獲取目標物件,呼叫 FactoryBean#getObject() 方法(反射機制) - 是否需要後續處理,這個 FactoryBean 的 BeanDefinition 是否由 Spring 解析出來的,通常情況下都是
- 對通過 FactoryBean 獲取的目標物件進行後置處理,遍歷所有的 BeanPostProcessor 的 postProcessAfterInitialization 方法(初始化的處理)
- 返回 FactoryBean 建立的目標物件,非單例模式不會進行快取
- 則根據
概括:呼叫 FactoryBean#getObject() 獲取目標物件,單例模式會快取起來;過程中 Sping 考慮到各種情況,例如保證單例模式下 FactoryBean#getObject() 只呼叫一次,是否需要進行後置處理。
4. 非單例模式依賴檢查
對應程式碼段:
// AbstractBeanFactory#doGetBean(...) 方法
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null && (curVal.equals(beanName) // 相等
|| (curVal instanceof Set && ((Set<?>) curVal).contains(beanName)))); // 包含
}
prototypesCurrentlyInCreation
中儲存非單例模式下正在建立的 Bean 的名稱,這裡又重新建立,表示出現迴圈依賴,則直接丟擲異常
Spring 對於非單例模式的 Bean 無法進行相關快取,也就無法處理迴圈依賴的情況,選擇了直接丟擲異常
5. BeanFactory 層次性載入 Bean 策略
對應程式碼段:
// AbstractBeanFactory#doGetBean(...) 方法
BeanFactory parentBeanFactory = getParentBeanFactory();
// <5> 如果從當前容器中沒有找到對應的 BeanDefinition,則從父容器中載入(如果存在父容器)
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
// <5.1> 獲取 `beanName`,因為可能是別名,則進行處理
// 和第 `1` 步不同,不需要對 `&` 進行處理,因為進入父容器重新依賴查詢
String nameToLookup = originalBeanName(name);
// <5.2> 若為 AbstractBeanFactory 型別,委託父容器的 doGetBean 方法進行處理
// 否則,就是非 Spring IoC 容器,根據引數呼叫相應的 `getBean(...)`方法
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
如果當前 BeanFactory 沒有對應的 BeanDefinition,也就無法建立 Bean,但是如果存在父 BeanFactory,則將接下來的操作交由父 BeanFactory,找不到會層層找上去,如果所有 BeanFactory 都找不到對應的 BeanDefinition 最終會丟擲異常。
6. 將 beanName 標記為已建立
對應程式碼段:
// AbstractBeanFactory#doGetBean(...) 方法
// <6> 如果不是僅僅做型別檢查,則表示需要建立 Bean,將 `beanName` 標記為已建立過
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
如果不是僅僅做型別檢查,則呼叫 markBeanAsCreated(String beanName)
方法,如下:
// AbstractBeanFactory.java
protected void markBeanAsCreated(String beanName) {
// 沒有建立
if (!this.alreadyCreated.contains(beanName)) {
// 加上全域性鎖
synchronized (this.mergedBeanDefinitions) {
// 再次檢查一次:DCL 雙檢查模式
if (!this.alreadyCreated.contains(beanName)) {
// Let the bean definition get re-merged now that we're actually creating
// the bean... just in case some of its metadata changed in the meantime.
// 從 mergedBeanDefinitions 中刪除 beanName,並在下次訪問時重新建立它
clearMergedBeanDefinition(beanName);
// 新增到已建立 bean 集合中
this.alreadyCreated.add(beanName);
}
}
}
}
將這個 beanName
儲存在 alreadyCreated
集合中(SetFromMap),在後面的迴圈依賴檢查中會使用到
7. 獲取 RootBeanDefinition
對應程式碼段:
// AbstractBeanFactory#doGetBean(...) 方法
// <7> 從容器中獲取 `beanName` 對應的的 RootBeanDefinition(合併後)
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 檢查是否為抽象類
checkMergedBeanDefinition(mbd, beanName, args);
因為我們定義的 Bean 大多數都被 Spring 解析成 GenericBeanDefinition 型別,具有父子關係,則需要獲取最終的 BeanDefinition;如果存在父子關係,則會進行一系列的合併,轉換成 RootBeanDefinition 物件,呼叫 getMergedLocalBeanDefinition(String beanName)
方法,如下:
// AbstractBeanFactory.java
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
// Quick check on the concurrent map first, with minimal locking.
// 從 `mergedBeanDefinitions` 快取中獲取合併後的 RootBeanDefinition,存在則直接返回
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
if (mbd != null) {
return mbd;
}
// 獲取 BeanDefinition 並轉換成,如果存在父子關係則進行合併
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
throws BeanDefinitionStoreException {
return getMergedBeanDefinition(beanName, bd, null);
}
protected RootBeanDefinition getMergedBeanDefinition(
String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
throws BeanDefinitionStoreException {
// 加鎖
synchronized (this.mergedBeanDefinitions) {
RootBeanDefinition mbd = null;
// Check with full lock now in order to enforce the same merged instance.
if (containingBd == null) {
mbd = this.mergedBeanDefinitions.get(beanName);
}
if (mbd == null) {
// 如果沒有父類則直接轉換成 RootBeanDefinition 物件
if (bd.getParentName() == null) {
// Use copy of given root bean definition.
if (bd instanceof RootBeanDefinition) {
mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
}
else {
mbd = new RootBeanDefinition(bd);
}
}
// 有父類則進行合併
else {
// Child bean definition: needs to be merged with parent.
BeanDefinition pbd;
try {
// 獲取父類的對應的 BeanDefinition 物件
String parentBeanName = transformedBeanName(bd.getParentName());
if (!beanName.equals(parentBeanName)) {
pbd = getMergedBeanDefinition(parentBeanName);
}
else {
BeanFactory parent = getParentBeanFactory();
if (parent instanceof ConfigurableBeanFactory) {
pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
}
else {
throw new NoSuchBeanDefinitionException(parentBeanName,
"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
"': cannot be resolved without an AbstractBeanFactory parent");
}
}
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
"Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
}
// Deep copy with overridden values.
mbd = new RootBeanDefinition(pbd);
// 父子合併
mbd.overrideFrom(bd);
}
// Set default singleton scope, if not configured before.
if (!StringUtils.hasLength(mbd.getScope())) {
mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
}
// A bean contained in a non-singleton bean cannot be a singleton itself.
// Let's correct this on the fly here, since this might be the result of
// parent-child merging for the outer bean, in which case the original inner bean
// definition will not have inherited the merged outer bean's singleton status.
if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
mbd.setScope(containingBd.getScope());
}
// Cache the merged bean definition for the time being
// (it might still get re-merged later on in order to pick up metadata changes)
if (containingBd == null && isCacheBeanMetadata()) {
// 放入快取中
this.mergedBeanDefinitions.put(beanName, mbd);
}
}
return mbd;
}
}
過程大致如下:
- 從
mergedBeanDefinitions
快取中獲取合併後的 RootBeanDefinition,存在則直接返回,不存在則進行後面的操作 - 獲取合併後的 RootBeanDefinition 物件,邏輯並不複雜,將一些屬性進行合併;這裡對於父 BeanDefinition 的獲取也存在層次性查詢策略;注意,如果一個單例 BeanDefinition 包含在非單例 BeanDefinition,那麼會變成非單例 Bean
後續還會對合並後的 RootBeanDefinition 物件進行檢查,如果是抽象的,則丟擲異常
8. 依賴 Bean 的處理
對應程式碼段:
// AbstractBeanFactory#doGetBean(...) 方法
// Guarantee initialization of beans that the current bean depends on.
// <8> 獲取當前正在建立的 Bean 所依賴物件集合(`depends-on` 配置的依賴)
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
// <8.1> 檢測是否存在迴圈依賴,存在則丟擲異常
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// <8.2> 將 `beanName` 與 `dep` 之間依賴的關係進行快取
registerDependentBean(dep, beanName);
try {
// <8.3> 先建立好依賴的 Bean(重新呼叫 `getBean(...)` 方法)
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
-
每個 Bean 不一定是單獨工作的,可以通過
depends-on
配置依賴的 Bean,其他 Bean 也可以依賴它 -
對於依賴的 Bean,會優先載入,所以在 Spring 的載入順序中,在初始化某個 Bean 的時候,首先會初始化這個 Bean 的依賴
isDependent 方法
在初始化依賴的 Bean 之前,會呼叫 isDependent(String beanName, String dependentBeanName)
方法,判斷是否出現迴圈依賴,方法如下:
DefaultSingletonBeanRegistry.java
protected boolean isDependent(String beanName, String dependentBeanName) {
synchronized (this.dependentBeanMap) {
return isDependent(beanName, dependentBeanName, null);
}
}
private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
// <1> `alreadySeen` 中已經檢測過該 `beanName` 則直接返回 `false`
if (alreadySeen != null && alreadySeen.contains(beanName)) {
return false;
}
// <2> 獲取最終的 `beanName`,因為可能是別名,需要進行相關處理
String canonicalName = canonicalName(beanName);
// <3> 從 `dependentBeanMap` 中獲取依賴 `beanName` 的 Bean 集合
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
// <4> 沒有 Bean 依賴該 `beanName`,也就不存在迴圈依賴,返回 `false`
if (dependentBeans == null) {
return false;
}
// <5> 依賴 `beanName` 的 Bean 們包含 `dependentBeanName`,表示出現迴圈依賴,返回 `true`
if (dependentBeans.contains(dependentBeanName)) {
// `beanName` 與 `dependentBeanName` 相互依賴
return true;
}
// <6> 對依賴該 `beanName` 的 Bean 們進行檢查,看它們是否與 `dependentBeanName` 存在依賴,遞迴處理
for (String transitiveDependency : dependentBeans) {
if (alreadySeen == null) {
alreadySeen = new HashSet<>();
}
alreadySeen.add(beanName);
if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
return true;
}
}
return false;
}
過程大致如下:
alreadySeen
中已經檢測過該beanName
則直接返回false
- 獲取最終的
beanName
,因為可能是別名,需要進行相關處理 - 從
dependentBeanMap
中獲取依賴beanName
的 Bean 集合 - 沒有 Bean 依賴該
beanName
,也就不存在迴圈依賴,返回false
- 依賴
beanName
的 Bean 們包含dependentBeanName
,表示出現迴圈依賴,返回true
- 對依賴該
beanName
的 Bean 們進行檢查,看它們是否與dependentBeanName
存在依賴,遞迴處理
判斷是否出現迴圈依賴的過程有點繞,需要花點時間理解一下。例如:現在檢查 A ->(依賴)B,看是否出現迴圈依賴,我獲取到依賴 A 的所有 Bean,看 B 是否依賴這裡面的 Bean,如果出現 A -> B -> C -> A,那就出現迴圈依賴了。如果出現迴圈依賴,則會丟擲異常,所以我們說 Spring 處理了單例 Bean 的迴圈依賴注入比較好一點。
registerDependentBean 方法
將 beanName
與 dep
(beanName
的依賴)之間依賴的關係進行快取,呼叫 registerDependentBean(String beanName, String dependentBeanName)
方法,如下:
DefaultSingletonBeanRegistry.java
public void registerDependentBean(String beanName, String dependentBeanName) {
String canonicalName = canonicalName(beanName);
// 對應關係:beanName -> 依賴 beanName 的集合
synchronized (this.dependentBeanMap) {
Set<String> dependentBeans = this.dependentBeanMap.computeIfAbsent(canonicalName,
k -> new LinkedHashSet<>(8));
if (!dependentBeans.add(dependentBeanName)) {
return;
}
}
// 對應關係:beanName - > beanName 的依賴的集合
synchronized (this.dependenciesForBeanMap) {
Set<String> dependenciesForBean = this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName,
k -> new LinkedHashSet<>(8));
dependenciesForBean.add(canonicalName);
}
}
將兩者的依賴關係儲存起來,目的是在 isDependent
方法中判斷是否出現迴圈依賴
getBean 方法
載入 beanName
依賴的 Bean,同樣是呼叫 AbstractBeanFactory#getBean(String dep)
方法,也就是本文開頭講的這個方法
9. 不同作用域的 Bean 的建立
Spring 的作用域劃分為三種:單例模式、原型模式、其他模式,會依次進行判斷,然後進行建立,建立過程都是一樣的,主要是儲存範圍不一樣
- 單例模式:一個 BeanFactory 有且僅有一個例項
- 原型模式:每次依賴查詢和依賴注入生成新 Bean 物件
- 其他模式,例如
request
作用域會將 Bean 儲存在 ServletRequest 上下文中;session
作用域會將 Bean 儲存在 HttpSession 中;application
作用域會將 Bean 儲存在 ServletContext 中
單例模式
對應程式碼段:
// AbstractBeanFactory#doGetBean(...) 方法
if (mbd.isSingleton()) { // <9.1> 單例模式
/*
* <9.1.1> 建立 Bean,成功建立則進行快取,並移除快取的早期物件
* 建立過程實際呼叫的下面這個 `createBean(...)` 方法
*/
sharedInstance = getSingleton(beanName,
// ObjectFactory 實現類
() -> {
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;
}
});
// <9.1.2> 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接返回
// 否則,呼叫 FactoryBean#getObject() 獲取目標物件
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
如果是單例模式,建立過程大致如下:
-
呼叫
DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory<?> singletonFactory)
方法建立 Bean,成功建立則進行快取,並移除快取的早期物件,建立過程實際呼叫的下面這個
AbstractAutowireCapableBeanFactory#createBean(...)
方法 -
FactoryBean 的處理,在前面 3. FactoryBean 的處理 中已經分析過
getSingleton 方法
DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory<?> singletonFactory)
方法,單例模式下獲取單例 Bean,如下:
// DefaultSingletonBeanRegistry.java
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// 全域性加鎖
synchronized (this.singletonObjects) {
// <1> 從 `singletonObjects` 單例 Bean 的快取中獲取 Bean(再檢查一遍),存在則直接返回,否則開始建立
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
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 + "'");
}
// <2> 將 `beanName` 標記為單例模式正在建立
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
/**
* <3> 建立 Bean,實際呼叫
* {@link AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[])} 方法
*/
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;
}
// <4> 將 `beanName` 標記為不在建立中,照應第 `2` 步
afterSingletonCreation(beanName);
}
// <5> 如果這裡是新建立的單例模式 Bean,則在 `singletonObjects` 中進行快取(無序),移除快取的早期物件
// 並在 `registeredSingletons` 中儲存 `beanName`,保證註冊順序
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
過程大致如下:
- 從
singletonObjects
單例 Bean 的快取中獲取 Bean(再檢查一遍),存在則直接返回,否則開始建立 - 將
beanName
標記為單例模式正在建立 - 【核心】建立 Bean,實際呼叫
AbstractAutowireCapableBeanFactory#createBean(...)
方法 - 將
beanName
標記為不在建立中,照應第2
步 - 如果這裡是新建立的單例模式 Bean,則在
singletonObjects
中進行快取(無序),移除快取的早期物件,並在registeredSingletons
中儲存beanName
,保證註冊順序
createBean 方法
AbstractAutowireCapableBeanFactory#createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
方法,建立 Bean,整個過程大致如下:
- Bean 的例項化
- 屬性賦值(包括依賴注入)
- Aware 介面回撥
- 呼叫初始化方法
上面涉及到 Bean 生命週期的大部分階段,將會在後續的文章中依次分析
原型模式
對應程式碼段:
// AbstractBeanFactory.java
// <9.2> 原型模式
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
// <9.2.1> 將 `beanName` 標記為**非單例模式**正在建立
beforePrototypeCreation(beanName);
// <9.2.2> **【核心】** 建立 Bean
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
// <9.2.3> 將 `beanName` 標記為不在建立中,照應第 `9.2.1` 步
afterPrototypeCreation(beanName);
}
// <9.2.4> 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接返回
// 否則,呼叫 FactoryBean#getObject() 獲取目標物件
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
過程大致如下:
- 將
beanName
標記為非單例模式正在建立 - 【核心】建立 Bean 也是呼叫
AbstractAutowireCapableBeanFactory#createBean(...)
方法,這裡沒有快取,每次載入 Bean 都會建立一個物件 - 將
beanName
標記為不在建立中,照應第1
步 - FactoryBean 的處理,在前面 3. FactoryBean 的處理 中已經分析過
其他模式
對應程式碼段:
// AbstractBeanFactory.java
// <9.3> 其他模式
else {
// <9.3.1> 獲取該模式的 Scope 物件 `scope`,不存在則丟擲異常
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 {
// <9.3.2> 從 `scope` 中獲取 `beanName` 對應的物件(看你的具體實現),不存在則執行**原型模式**的四個步驟進行建立
Object scopedInstance = scope.get(beanName, () -> {
// 將 `beanName` 標記為**非單例模式**式正在建立
beforePrototypeCreation(beanName);
try {
// **【核心】** 建立 Bean
return createBean(beanName, mbd, args);
}
finally {
// 將 `beanName` 標記為不在建立中,照應上一步
afterPrototypeCreation(beanName);
}
});
// 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接返回
// 否則,呼叫 FactoryBean#getObject() 獲取目標物件
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
,不存在則丟擲異常 - 從
scope
中獲取beanName
對應的物件(看你的具體實現),不存在則執行原型模式的四個步驟進行建立
想要自定義一個作用域,可以實現 org.springframework.beans.factory.config.Scope
介面,並往 Spring 應用上下文註冊即可
10. 型別轉換
對應程式碼段:
// AbstractBeanFactory#doGetBean(...) 方法
// <10> 如果入參 `requiredType` 不為空,並且 Bean 不是該型別,則需要進行型別轉換
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
// <10.1> 通過型別轉換機制,將 Bean 轉換成 `requiredType` 型別
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
// <10.2> 轉換後的 Bean 為空則丟擲異常
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
// <10.3> 返回型別轉換後的 Bean 物件
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
如果入參 requiredType
不為空,並且 Bean 不是該型別,則需要進行型別轉換,過程如下:
- 通過型別轉換機制,將 Bean 轉換成
requiredType
型別,對 Spring 的型別轉換機制感興趣的小夥伴可以自己研究,參考org.springframework.core.convert.support.DefaultConversionService
- 轉換後的 Bean 為空則丟擲異常
- 返回型別轉換後的 Bean 物件
總結
本文對 BeanFactory 介面的體系結構進行了分析,得知 DefaultListableBeanFactory 是 BeanFactory 的最底層實現,也就是 Spring 的底層 IoC 容器。接著分析了 AbstractBeanFactory
的 getBean(...)
方法,當我們顯示或者隱式地呼叫這個方法時,會觸發 Bean 的載入。上面所有小節對 Bean 的載入過程進行了分析,我已經有序地在每個小節面前新增了序號,這些序號對應著載入過程中的順序。
不同作用域的 Bean 的建立,底層都會呼叫 AbstractAutowireCapableBeanFactory
的 createBean(...)
方法進行建立,建立 Bean 的過程涉及到 Bean 生命週期的大部分階段,例如例項化階段、屬性賦值階段、Aware 介面回撥階段、初始化階段都是在這個方法中完成的,整個建立過程將在後續的文章進行分析。