1. 簡介
為了寫 Spring IOC 容器源碼分析系列的文章,我特地寫了一篇 Spring IOC 容器的導讀文章。在導讀一文中,我介紹了 Spring 的一些特性以及閱讀 Spring 源碼的一些建議。在做完必要的准備工作后,從本文開始,正式開始進入源碼分析的階段。
在本篇文章中,我將會詳細分析BeanFactory
的getBean(String)
方法實現細節,getBean(String)
及所調用的方法總體來說實現上較為復雜,代碼長度比較長。作為源碼分析文章,本文的文章長度也會比較長,希望大家耐心讀下去。
好了,其他的不多說了,進入主題環節吧。
2. 源碼分析
簡單說一下本章的內容安排吧,在本章的開始,也就是2.1節,我將會分析getBean(String)
方法整體的實現邏輯。但不會分析它所調用的方法,這些方法將會在后續幾節中依次進行分析。那接下來,我們就先來看看 getBean(String) 方法是如何實現的吧。
2.1 俯瞰 getBean(String) 源碼
在本小節,我們先從戰略上俯瞰 getBean(String) 方法的實現源碼。代碼如下:
public Object getBean(String name) throws BeansException {
// getBean 是一個空殼方法,所有的邏輯都封裝在 doGetBean 方法中
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
/*
* 通過 name 獲取 beanName。這里不使用 name 直接作為 beanName 有兩點原因:
* 1. name 可能會以 & 字符開頭,表明調用者想獲取 FactoryBean 本身,而非 FactoryBean
* 實現類所創建的 bean。在 BeanFactory 中,FactoryBean 的實現類和其他的 bean 存儲
* 方式是一致的,即 <beanName, bean>,beanName 中是沒有 & 這個字符的。所以我們需要
* 將 name 的首字符 & 移除,這樣才能從緩存里取到 FactoryBean 實例。
* 2. 若 name 是一個別名,則應將別名轉換為具體的實例名,也就是 beanName。
*/
final String beanName = transformedBeanName(name);
Object bean;
/*
* 從緩存中獲取單例 bean。Spring 是使用 Map 作為 beanName 和 bean 實例的緩存的,所以這
* 里暫時可以把 getSingleton(beanName) 等價於 beanMap.get(beanName)。當然,實際的
* 邏輯並非如此簡單,后面再細說。
*/
Object sharedInstance = getSingleton(beanName);
/*
* 如果 sharedInstance = null,則說明緩存里沒有對應的實例,表明這個實例還沒創建。
* BeanFactory 並不會在一開始就將所有的單例 bean 實例化好,而是在調用 getBean 獲取
* bean 時再實例化,也就是懶加載。
* getBean 方法有很多重載,比如 getBean(String name, Object... args),我們在首次獲取
* 某個 bean 時,可以傳入用於初始化 bean 的參數數組(args),BeanFactory 會根據這些參數
* 去匹配合適的構造方法構造 bean 實例。當然,如果單例 bean 早已創建好,這里的 args 就沒有
* 用了,BeanFactory 不會多次實例化單例 bean。
*/
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 + "'");
}
}
/*
* 如果 sharedInstance 是普通的單例 bean,下面的方法會直接返回。但如果
* sharedInstance 是 FactoryBean 類型的,則需調用 getObject 工廠方法獲取真正的
* bean 實例。如果用戶想獲取 FactoryBean 本身,這里也不會做特別的處理,直接返回
* 即可。畢竟 FactoryBean 的實現類本身也是一種 bean,只不過具有一點特殊的功能而已。
*/
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
/*
* 如果上面的條件不滿足,則表明 sharedInstance 可能為空,此時 beanName 對應的 bean
* 實例可能還未創建。這里還存在另一種可能,如果當前容器有父容器,beanName 對應的 bean 實例
* 可能是在父容器中被創建了,所以在創建實例前,需要先去父容器里檢查一下。
*/
else {
// BeanFactory 不緩存 Prototype 類型的 bean,無法處理該類型 bean 的循環依賴問題
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 如果 sharedInstance = null,則到父容器中查找 bean 實例
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 獲取 name 對應的 beanName,如果 name 是以 & 字符開頭,則返回 & + beanName
String nameToLookup = originalBeanName(name);
// 根據 args 是否為空,以決定調用父容器哪個方法獲取 bean
if (args != null) {
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
// 合並父 BeanDefinition 與子 BeanDefinition,后面會單獨分析這個方法
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 檢查是否有 dependsOn 依賴,如果有則先初始化所依賴的 bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
/*
* 檢測是否存在 depends-on 循環依賴,若存在則拋異常。比如 A 依賴 B,
* B 又依賴 A,他們的配置如下:
* <bean id="beanA" class="BeanA" depends-on="beanB">
* <bean id="beanB" class="BeanB" depends-on="beanA">
*
* beanA 要求 beanB 在其之前被創建,但 beanB 又要求 beanA 先於它
* 創建。這個時候形成了循環,對於 depends-on 循環,Spring 會直接
* 拋出異常
*/
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 注冊依賴記錄
registerDependentBean(dep, beanName);
try {
// 加載 depends-on 依賴
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// 創建 bean 實例
if (mbd.isSingleton()) {
/*
* 這里並沒有直接調用 createBean 方法創建 bean 實例,而是通過
* getSingleton(String, ObjectFactory) 方法獲取 bean 實例。
* getSingleton(String, ObjectFactory) 方法會在內部調用
* ObjectFactory 的 getObject() 方法創建 bean,並會在創建完成后,
* 將 bean 放入緩存中。關於 getSingleton 方法的分析,本文先不展開,我會在
* 后面的文章中進行分析
*/
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
// 創建 bean 實例
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
// 如果 bean 是 FactoryBean 類型,則調用工廠方法獲取真正的 bean 實例。否則直接返回 bean 實例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 創建 prototype 類型的 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 實例
else {
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, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
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);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// 如果需要進行類型轉換,則在此處進行轉換。類型轉換這一塊我沒細看,就不多說了。
if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
// 返回 bean
return (T) bean;
}
以上就是getBean(String)
和doGetBean(String, Class, Object[], boolean)
兩個方法的分析。代碼很長,需要一點耐心閱讀。為了凸顯方法的主邏輯,大家可以對代碼進行一定的刪減,刪除一些日志和異常代碼,也可以刪除一些不是很重要的邏輯。另外由於 doGetBean 方法調用了其他的很多方法,在看代碼時,經常會忘掉 doGetBean 所調用的方法是怎么實現的。比如 getSingleton 方法出現了兩次,但兩個方法並不同,在看第二個的 getSingleton 方法時,可能會忘掉第一個 getSingleton 是怎么實現的。另外,如果你想對比兩個重載方法的異同,在 IDEA 里跳來跳去也是很不方便。為此,我使用了 sublime 進行分屏,左屏是刪減后的 doGetBean 方法,右屏是 doGetBean 調用的一些方法,這樣看起來會方便一點。忘了某個方法的實現邏輯后,可以到右屏查看,也可進行對比。分屏效果如下:
這里我為了演示,刪除了不少東西。大家可以按需進行刪減,並配上注釋,輔助理解。
看完了源碼,下面我來簡單總結一下 doGetBean 的執行流程。如下:
- 轉換 beanName
- 從緩存中獲取實例
- 如果實例不為空,且 args = null。調用 getObjectForBeanInstance 方法,並按 name 規則返回相應的 bean 實例
- 若上面的條件不成立,則到父容器中查找 beanName 對有的 bean 實例,存在則直接返回
- 若父容器中不存在,則進行下一步操作 -- 合並 BeanDefinition
- 處理 depends-on 依賴
- 創建並緩存 bean
- 調用 getObjectForBeanInstance 方法,並按 name 規則返回相應的 bean 實例
- 按需轉換 bean 類型,並返回轉換后的 bean 實例。
以上步驟對應的流程圖如下:
2.2 beanName 轉換
在獲取 bean 實例之前,Spring 第一件要做的事情是對參數 name 進行轉換。轉換的目的主要是為了解決兩個問題,第一個是處理以字符 & 開頭的 name,防止 BeanFactory 無法找到與 name 對應的 bean 實例。第二個是處理別名問題,Spring 不會存儲 <別名, bean 實例> 這種映射,僅會存儲 <beanName, bean>。所以,同樣是為了避免 BeanFactory 找不到 name 對應的 bean 的實例,對於別名也要進行轉換。接下來,我們來簡單分析一下轉換的過程,如下:
protected String transformedBeanName(String name) {
// 這里調用了兩個方法:BeanFactoryUtils.transformedBeanName(name) 和 canonicalName
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
/** 該方法用於處理 & 字符 */
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
String beanName = name;
// 循環處理 & 字符。比如 name = "&&&&&helloService",最終會被轉成 helloService
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
return beanName;
}
/** 該方法用於轉換別名 */
public String canonicalName(String name) {
String canonicalName = name;
String resolvedName;
/*
* 這里使用 while 循環進行處理,原因是:可能會存在多重別名的問題,即別名指向別名。比如下面
* 的配置:
* <bean id="hello" class="service.Hello"/>
* <alias name="hello" alias="aliasA"/>
* <alias name="aliasA" alias="aliasB"/>
*
* 上面的別名指向關系為 aliasB -> aliasA -> hello,對於上面的別名配置,aliasMap 中數據
* 視圖為:aliasMap = [<aliasB, aliasA>, <aliasA, hello>]。通過下面的循環解析別名
* aliasB 最終指向的 beanName
*/
do {
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}
2.3 從緩存中獲取 bean 實例
對於單例 bean,Spring 容器只會實例化一次。后續再次獲取時,只需直接從緩存里獲取即可,無需且不能再次實例化(否則單例就沒意義了)。從緩存中取 bean 實例的方法是getSingleton(String)
,下面我們就來看看這個方法實現方式吧。如下:
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
/**
* 這里解釋一下 allowEarlyReference 參數,allowEarlyReference 表示是否允許其他 bean 引用
* 正在創建中的 bean,用於處理循環引用的問題。關於循環引用,這里先簡單介紹一下。先看下面的配置:
*
* <bean id="hello" class="xyz.coolblog.service.Hello">
* <property name="world" ref="world"/>
* </bean>
* <bean id="world" class="xyz.coolblog.service.World">
* <property name="hello" ref="hello"/>
* </bean>
*
* 如上所示,hello 依賴 world,world 又依賴於 hello,他們之間形成了循環依賴。Spring 在構建
* hello 這個 bean 時,會檢測到它依賴於 world,於是先去實例化 world。實例化 world 時,發現
* world 依賴 hello。這個時候容器又要去初始化 hello。由於 hello 已經在初始化進程中了,為了讓
* world 能完成初始化,這里先讓 world 引用正在初始化中的 hello。world 初始化完成后,hello
* 就可引用到 world 實例,這樣 hello 也就能完成初始了。關於循環依賴,我后面會專門寫一篇文章講
* 解,這里先說這么多。
*/
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 從 singletonObjects 獲取實例,singletonObjects 中緩存的實例都是完全實例化好的 bean,可以直接使用
Object singletonObject = this.singletonObjects.get(beanName);
/*
* 如果 singletonObject = null,表明還沒創建,或者還沒完全創建好。
* 這里判斷 beanName 對應的 bean 是否正在創建中
*/
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 從 earlySingletonObjects 中獲取提前曝光的 bean,用於處理循環引用
singletonObject = this.earlySingletonObjects.get(beanName);
// 如果如果 singletonObject = null,且允許提前曝光 bean 實例,則從相應的 ObjectFactory 獲取一個原始的(raw)bean(尚未填充屬性)
if (singletonObject == null && allowEarlyReference) {
// 獲取相應的工廠類
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 提前曝光 bean 實例,用於解決循環依賴
singletonObject = singletonFactory.getObject();
// 放入緩存中,如果還有其他 bean 依賴當前 bean,其他 bean 可以直接從 earlySingletonObjects 取結果
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
上面的代碼雖然不長,但是涉及到了好幾個緩存集合。如果不知道這些緩存的用途是什么,上面源碼可能就很難弄懂了。這幾個緩存集合用的很頻繁,在后面的代碼中還會出現,所以這里介紹一下。如下:
緩存 | 用途 |
---|---|
singletonObjects | 用於存放完全初始化好的 bean,從該緩存中取出的 bean 可以直接使用 |
earlySingletonObjects | 用於存放還在初始化中的 bean,用於解決循環依賴 |
singletonFactories | 用於存放 bean 工廠。bean 工廠所產生的 bean 是還未完成初始化的 bean。如代碼所示,bean 工廠所生成的對象最終會被緩存到 earlySingletonObjects 中 |
關於 getSingleton 先說到這里,getSingleton 源碼並不多。但涉及到了循環依賴的相關邏輯,如果對這一塊不理解可能不知道代碼所雲。等后面分析循環依賴的時候,我會再次分析這個方法,所以暫時不理解也沒關系。
2.4 合並父 BeanDefinition 與子 BeanDefinition
Spring 支持配置繼承,在
parent
屬性配置父類 bean。這樣子類 bean 可以繼承父類 bean 的配置信息,同時也可覆蓋父類中的配置。比如下面的配置:
<bean id="hello" class="xyz.coolblog.innerbean.Hello">
<property name="content" value="hello"/>
</bean>
<bean id="hello-child" parent="hello">
<property name="content" value="I`m hello-child"/>
</bean>
如上所示,hello-child 配置繼承自 hello。hello-child 未配置 class 屬性,這里我們讓它繼承父配置中的 class 屬性。然后我們寫點代碼測試一下,如下:
String configLocation = "application-parent-bean.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);
System.out.println("hello -> " + applicationContext.getBean("hello"));
System.out.println("hello-child -> " + applicationContext.getBean("hello-child"));
測試結果如下:
由測試結果可以看出,hello-child 在未配置 class 的屬性下也實例化成功了,表明它成功繼承了父配置的 class 屬性。
看完代碼演示,接下來我們來看看源碼吧。如下:
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
// 檢查緩存中是否存在“已合並的 BeanDefinition”,若有直接返回即可
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
if (mbd != null) {
return mbd;
}
// 調用重載方法
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, BeanDefinition containingBd)
throws BeanDefinitionStoreException {
synchronized (this.mergedBeanDefinitions) {
RootBeanDefinition mbd = null;
// 我暫時還沒去詳細了解 containingBd 的用途,盡管從方法的注釋上可以知道 containingBd 的大致用途,但沒經過詳細分析,就不多說了。見諒
if (containingBd == null) {
mbd = this.mergedBeanDefinitions.get(beanName);
}
if (mbd == null) {
// bd.getParentName() == null,表明無父配置,這時直接將當前的 BeanDefinition 升級為 RootBeanDefinition
if (bd.getParentName() == null) {
if (bd instanceof RootBeanDefinition) {
mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
}
else {
mbd = new RootBeanDefinition(bd);
}
}
else {
BeanDefinition pbd;
try {
String parentBeanName = transformedBeanName(bd.getParentName());
/*
* 判斷父類 beanName 與子類 beanName 名稱是否相同。若相同,則父類 bean 一定
* 在父容器中。原因也很簡單,容器底層是用 Map 緩存 <beanName, bean> 鍵值對
* 的。同一個容器下,使用同一個 beanName 映射兩個 bean 實例顯然是不合適的。
* 有的朋友可能會覺得可以這樣存儲:<beanName, [bean1, bean2]> ,似乎解決了
* 一對多的問題。但是也有問題,調用 getName(beanName) 時,到底返回哪個 bean
* 實例好呢?
*/
if (!beanName.equals(parentBeanName)) {
/*
* 這里再次調用 getMergedBeanDefinition,只不過參數值變為了
* parentBeanName,用於合並父 BeanDefinition 和爺爺輩的
* BeanDefinition。如果爺爺輩的 BeanDefinition 仍有父
* BeanDefinition,則繼續合並
*/
pbd = getMergedBeanDefinition(parentBeanName);
}
else {
// 獲取父容器,並判斷,父容器的類型,若不是 ConfigurableBeanFactory 則判拋出異常
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);
}
// 以父 BeanDefinition 的配置信息為藍本創建 RootBeanDefinition,也就是“已合並的 BeanDefinition”
mbd = new RootBeanDefinition(pbd);
// 用子 BeanDefinition 中的屬性覆蓋父 BeanDefinition 中的屬性
mbd.overrideFrom(bd);
}
// 如果用戶未配置 scope 屬性,則默認將該屬性配置為 singleton
if (!StringUtils.hasLength(mbd.getScope())) {
mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
}
if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
mbd.setScope(containingBd.getScope());
}
if (containingBd == null && isCacheBeanMetadata()) {
// 緩存合並后的 BeanDefinition
this.mergedBeanDefinitions.put(beanName, mbd);
}
}
return mbd;
}
}
上面的源碼雖然有點長,但好在邏輯不是很復雜。加上我在源碼里進行了比較詳細的注解,我想耐心看一下還是可以看懂的,這里就不多說了。
2.5 從 FactoryBean 中獲取 bean 實例
在經過前面這么多的步驟處理后,到這里差不多就接近 doGetBean 方法的尾聲了。在本節中,我們來看看從 FactoryBean 實現類中獲取 bean 實例的過程。關於 FactoryBean 的用法,我在導讀那篇文章中已經演示過,這里就不再次說明了。那接下來,我們直入主題吧,相關的源碼如下:
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
// 如果 name 以 & 開頭,但 beanInstance 卻不是 FactoryBean,則認為有問題。
if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}
/*
* 如果上面的判斷通過了,表明 beanInstance 可能是一個普通的 bean,也可能是一個
* FactoryBean。如果是一個普通的 bean,這里直接返回 beanInstance 即可。如果是
* FactoryBean,則要調用工廠方法生成一個 bean 實例。
*/
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
Object object = null;
if (mbd == null) {
/*
* 如果 mbd 為空,則從緩存中加載 bean。FactoryBean 生成的單例 bean 會被緩存
* 在 factoryBeanObjectCache 集合中,不用每次都創建
*/
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// 經過前面的判斷,到這里可以保證 beanInstance 是 FactoryBean 類型的,所以可以進行類型轉換
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// 如果 mbd 為空,則判斷是否存在名字為 beanName 的 BeanDefinition
if (mbd == null && containsBeanDefinition(beanName)) {
// 合並 BeanDefinition
mbd = getMergedLocalBeanDefinition(beanName);
}
// synthetic 字面意思是"合成的"。通過全局查找,我發現在 AOP 相關的類中會將該屬性設為 true。
// 所以我覺得該字段可能表示某個 bean 是不是被 AOP 增強過,也就是 AOP 基於原始類合成了一個新的代理類。
// 不過目前只是猜測,沒有深究。如果有朋友知道這個字段的具體意義,還望不吝賜教
boolean synthetic = (mbd != null && mbd.isSynthetic());
// 調用 getObjectFromFactoryBean 方法繼續獲取實例
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
/*
* FactoryBean 也有單例和非單例之分,針對不同類型的 FactoryBean,這里有兩種處理方式:
* 1. 單例 FactoryBean 生成的 bean 實例也認為是單例類型。需放入緩存中,供后續重復使用
* 2. 非單例 FactoryBean 生成的 bean 實例則不會被放入緩存中,每次都會創建新的實例
*/
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
// 從緩存中取 bean 實例,避免多次創建 bean 實例
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// 使用工廠對象中創建實例
object = doGetObjectFromFactoryBean(factory, beanName);
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
}
else {
// shouldPostProcess 等價於上一個方法中的 !synthetic,用於表示是否應用后置處理
if (object != null && shouldPostProcess) {
if (isSingletonCurrentlyInCreation(beanName)) {
return object;
}
beforeSingletonCreation(beanName);
try {
// 應用后置處理
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
}
finally {
afterSingletonCreation(beanName);
}
}
// 這里的 beanName 對應於 FactoryBean 的實現類, FactoryBean 的實現類也會被實例化,並被緩存在 singletonObjects 中
if (containsSingleton(beanName)) {
// FactoryBean 所創建的實例會被緩存在 factoryBeanObjectCache 中,供后續調用使用
this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
}
}
}
return (object != NULL_OBJECT ? object : null);
}
}
// 獲取非單例實例
else {
// 從工廠類中獲取實例
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (object != null && shouldPostProcess) {
try {
// 應用后置處理
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
return object;
}
}
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
throws BeanCreationException {
Object object;
try {
// if 分支的邏輯是 Java 安全方面的代碼,可以忽略,直接看 else 分支的代碼
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
return factory.getObject();
}
}, acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
// 調用工廠方法生成 bean 實例
object = factory.getObject();
}
}
catch (FactoryBeanNotInitializedException ex) {
throw new BeanCurrentlyInCreationException(beanName, ex.toString());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}
if (object == null && isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(
beanName, "FactoryBean which is currently in creation returned null from getObject");
}
return object;
}
上面的源碼分析完了,代碼雖長,但整體邏輯不是很復雜,這里簡單總結一下。getObjectForBeanInstance 及它所調用的方法主要做了如下幾件事情:
- 檢測參數 beanInstance 的類型,如果是非 FactoryBean 類型的 bean,直接返回
- 檢測 FactoryBean 實現類是否單例類型,針對單例和非單例類型進行不同處理
- 對於單例 FactoryBean,先從緩存里獲取 FactoryBean 生成的實例
- 若緩存未命中,則調用 FactoryBean.getObject() 方法生成實例,並放入緩存中
- 對於非單例的 FactoryBean,每次直接創建新的實例即可,無需緩存
- 如果 shouldPostProcess = true,不管是單例還是非單例 FactoryBean 生成的實例,都要進行后置處理
本節涉及到了 FactoryBean 和后置處理兩個特性,關於這兩個特性,不熟悉的同學可以參考我在導讀一文中的說明,這里就不過多解釋了。
3. 總結
到這里,Spring IOC 容器獲取 bean 實例這一塊的內容就分析完了。如果大家是初次閱讀 Spring 的源碼,看不懂也沒關系。多看幾遍,認證思考一下,相信是能看得懂的。另外由於本人水平有限,以上的源碼分析有誤的地方,還望多指教,謝了。
好了,本文先到這里。又到周五了,祝大家在即將到來的周末玩的開心。over.
參考
附錄:Spring 源碼分析文章列表
Ⅰ. IOC
更新時間 | 標題 |
---|---|
2018-05-30 | Spring IOC 容器源碼分析系列文章導讀 |
2018-06-01 | Spring IOC 容器源碼分析 - 獲取單例 bean |
2018-06-04 | Spring IOC 容器源碼分析 - 創建單例 bean 的過程 |
2018-06-06 | Spring IOC 容器源碼分析 - 創建原始 bean 對象 |
2018-06-08 | Spring IOC 容器源碼分析 - 循環依賴的解決辦法 |
2018-06-11 | Spring IOC 容器源碼分析 - 填充屬性到 bean 原始對象 |
2018-06-11 | Spring IOC 容器源碼分析 - 余下的初始化工作 |
Ⅱ. AOP
更新時間 | 標題 |
---|---|
2018-06-17 | Spring AOP 源碼分析系列文章導讀 |
2018-06-20 | Spring AOP 源碼分析 - 篩選合適的通知器 |
2018-06-20 | Spring AOP 源碼分析 - 創建代理對象 |
2018-06-22 | Spring AOP 源碼分析 - 攔截器鏈的執行過程 |
Ⅲ. MVC
更新時間 | 標題 |
---|---|
2018-06-29 | Spring MVC 原理探秘 - 一個請求的旅行過程 |
2018-06-30 | Spring MVC 原理探秘 - 容器的創建過程 |
本文在知識共享許可協議 4.0 下發布,轉載需在明顯位置處注明出處
作者:田小波
本文同步發布在我的個人博客:http://www.tianxiaobo.com
本作品采用知識共享署名-非商業性使用-禁止演繹 4.0 國際許可協議進行許可。