前言
看過Spring解析前兩篇文章的讀者知道领迈,之前分析的思路一直都源自ClassPathXmlApplicationContext
的初始化,但該類的初始化還未分析完畢為什么突然切成bean
的加載呢暇昂?實際上在ClassPathXmlApplicationContext
初始化剩下的部分中多次進行了bean
的加載和獲取,當(dāng)然該流程是可以放在初始化過程中一并分析的,但bean
加載的流程比較復(fù)雜,如果將該流程和初始化過程放在一起無疑加重了原本就難于理解的邏輯,此外惰匙,從容器中獲取對象是Spring最常用的功能技掏,單獨的剝離分析更加有利于整體脈絡(luò)的構(gòu)建,因此另開一文單獨講解项鬼。bean
的加載由兩到三篇文章構(gòu)成哑梳,第一篇也就是本文,以顯式調(diào)用getBean
為切入點
在首篇開始的例子中可知绘盟,通常我們手動獲取bean
使用getBean
方法鸠真,該方法有幾種重載形式悯仙,最普遍的為Obejct getBean(String)
,根據(jù)beanName
得到實際類型為beanName
對應(yīng)bean
實例的Object
對象吠卷;另一種是我喜歡用的T getBean(Class<T>)
赏寇,這種方式省去了強轉(zhuǎn)過程营搅。不管任何一種獲取bean
的方式都殊途同歸,我們先以Obejct getBean(String)
為模板進行分析
ClassPathXmlApplicationContext
獲取bean
實際調(diào)用了其父類AbstractApplicationContext
中對應(yīng)方法。之前說過BeanFactory
的核心類為DefaultListableBeanFactory
姊扔,所以這里的getBeanFactory()
返回的就是該類對象。除了T getBean(Class<T>)
這種方式是在DefaultListableBeanFactory
中提供的集惋,其他獲取bean
的方法都封裝在另一個父類AbstractBeanFactory
中四種方式又同時抽成了
T doGetBean(String, Class<T>, Object[], boolean)
髓堪,該方法較長,見代碼清單1
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
// (1)
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// (2)
Object sharedInstance = getSingleton(beanName);
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 + "'");
}
}
// (3)
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
getBean(dependsOnBean);
registerDependentBean(dependsOnBean, beanName);
}
}
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
try {
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.
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
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;
}
}
// Check if required type matches the type of the actual bean instance.
if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
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());
}
}
return (T) bean;
}
從整體看上面的代碼可根據(jù)最頂層的if/else
分為上下兩部分搞坝,上半部分在調(diào)用getBean(String)
時進入搔谴,下半部分在new ClassPathXmlApplicationContext(String[])
時就會隱式執(zhí)行,下半部分進行首次初始化桩撮,上半部分從緩存中獲取敦第,通常情況隱式執(zhí)行早于顯示調(diào)用,雖然真實情況下存在誰先誰后等“時序”因素距境,但為了分析方便申尼,清楚表述,我在文章中都盡可能不考慮”時序“的影響垫桂,只關(guān)注邏輯流程师幕,由于執(zhí)行邏輯比較復(fù)雜,我們一點點分析诬滩。標(biāo)注1將name
進行了規(guī)范化處理霹粥,這里的name
存在多種可能:1.普通的id/name
;2.bean
的昵稱疼鸟;3.FactoryBean
對應(yīng)的id/name
后控,三種情況對應(yīng)三種不同的規(guī)范化方式,第一種不做處理直接返回空镜;第二種根據(jù)昵稱從SimpleAliasRegistry
中的成員變量Map<String, String> aliasMap
中獲得昵稱對應(yīng)的真實id/name
返回浩淘,至于昵稱是什么時候放入aliasMap
的,在Spring解析之IoC:<bean>解析及Bean的注冊的最后進行了bean
的注冊吴攒,該方法的是由BeanDefinitionReaderUtils
調(diào)用的
下劃線處就是bean
注冊的流程张抄,而紅框內(nèi)就是保存所有bean
對應(yīng)昵稱的過程,根據(jù)我們之前給過的DefaultListableBeanFactory
類圖可知洼怔,這里的registry
就是DefaultListableBeanFactory
署惯,但registerAlias(String, String)
定義在父類SimpleAliasRegistry
中,所有的alias
就放在了該類的成員變量aliasMap
中镣隶。至于第三種情況我們需要先知道FactoryBean
的用法和用途极谊,FactoryBean
可以看成一個實現(xiàn)了FactoryBean<T>
接口的特殊bean
诡右,它既可以是一個普通的bean
,也可以成為生產(chǎn)另一個bean
的工廠轻猖,當(dāng)創(chuàng)建一個FactoryBean
后帆吻,我們可以通過getBean("FactoryBean的id/name")
得到該FactoryBean
生產(chǎn)的普通bean
對象,如果就想得到該FactoryBean
實例行不行呢蜕依?答案當(dāng)然是可以的桅锄,我們需要這么寫getBean("&FactoryBean的id/name")
,下面我們舉個例子說明样眠,首先寫一個實現(xiàn)FactoryBean<T>
的工廠Bean
接下來在配置文件上加上對該類的配置友瘤,注意我沒有對Student
類進行XML配置
最后我們獲得Student
實例以及對應(yīng)的生產(chǎn)工廠實例
代碼清單1 標(biāo)注2根據(jù)beanName
獲得單例對象,但這里的單例對象不一定就是我們在配置文件中定義的bean
的實例檐束,為什么這么說我們進入方法看看
圖中標(biāo)記出了三個不同的“緩存”辫秧,所謂的緩存就是定義在DefaultSingletonBeanRegistry
的三個成員變量,該類和DefaultListableBeanFactory
是父子關(guān)系被丧,借用其他文章對這三個對象的稱謂盟戏,singletonObjects
為一級緩存,保存beanName
和bean
實例的對應(yīng)關(guān)系甥桂;earlySingletonObjects
是二級緩存柿究,也保存了beanName
和bean
的對應(yīng)關(guān)系。我們都知道假設(shè)A對象在初始化時依賴B對象黄选,Spring會在A對象初始化前先將B對象初始化蝇摸,本文將這種實例化完成并形成依賴關(guān)系的整個過程稱為完全初始化;將沒有建立依賴關(guān)系办陷,僅僅實例化完成的過程稱為不完全初始化貌夕,那這時解釋singletonObjects
和earlySingletonObjects
的區(qū)別就很簡單了,前者保存了完全初始化的實例映射民镜,后者保存未完全初始化的實例映射啡专;最后一個變量singletonFactories
是三級緩存,保存了beanName
和接口ObjectFactory
實現(xiàn)類的映射制圈,現(xiàn)在問題又來了ObjectFactory
是什么東東们童?我們可以將其理解為生產(chǎn)對象的工廠,和上面剛說到的FactoryBean
有幾分相似鲸鹦,之后我們會看到該類的具體實現(xiàn)和具體用法
和平時的認(rèn)知一樣慧库,三級緩存的調(diào)用依次深入,且相互排斥亥鬓,即一個beanName
只會存在于一個緩存中完沪,設(shè)置如此復(fù)雜的邏輯目的是盡可能的解決循環(huán)依賴問題域庇。解釋完后再看代碼就很清楚了嵌戈,先根據(jù)beanName
從一級緩存中獲取bean
覆积,如果沒有取到并且該對象正在創(chuàng)建isSingletonCurrentlyInCreation(String)
,就去二級緩存中查找熟呛。什么叫該對象正在創(chuàng)建呢宽档?該對象正在創(chuàng)建又和循環(huán)依賴什么關(guān)系呢?舉個例子:A對象依賴B對象庵朝,B對象依賴A對象吗冤,在創(chuàng)建A對象時要先創(chuàng)建B對象,在創(chuàng)建B對象時依賴A對象九府,A創(chuàng)建了一半等待B對象椎瘟,此時A對象的狀態(tài)就是正在創(chuàng)建,對于B對象也是一樣的侄旬。如果二級緩存中依然沒有肺蔚,且允許早期引用標(biāo)識allowEarlyReference = true
,就會從三級緩存中得到ObjectFactory
進而創(chuàng)建出最終的對象儡羔,創(chuàng)建完成后會將該對象存放在二級緩存中
代碼清單1 標(biāo)注3才是獲得真正bean
的方法
注意方法上注解對于傳參的解釋宣羊,
name
是沒有進過規(guī)范化處理的名稱,那么其中可能包含&
汰蜘,可能是昵稱仇冯,也可能就是普通的beanName
。第一處判斷name
是否包含工廠bean前綴&
且beanInstance
不屬于FactoryBean
族操,這種情況是不可能存在的苛坚,所以拋出異常;第二處判斷過濾掉兩種情況:1.普通的bean
坪创,前半部分為true炕婶,直接返回;2.只想獲得FactoryBean
莱预,后半部分為true柠掂,直接返回。只有想通過FactoryBean
獲得普通bean
時才程序才會繼續(xù)往下執(zhí)行另外我們需要說一下
RootBeanDefinition
依沮,之前在分析配置加載解析的過程中bean
對應(yīng)的實體一直為GenericBeanDefinition
涯贞,而這里又出來個RootBeanDefinition
,該類與前者不同之處在于危喉,前者可以稱為一般意義上的bean
宋渔,后者主要用于封裝存在繼承關(guān)系的bean
對象,比如<bean>
中存在parent
屬性就意味著存在root
和child
的關(guān)系辜限,在解析的時候Spring會將所有的父子關(guān)系merge
到一個BeanDefinition
中皇拣,而RootBeanDefinition
就能很好的表示bean
直接的父子關(guān)系。當(dāng)然現(xiàn)在Spring已經(jīng)推薦使用GenericBeanDefinition
,該類同樣可以封裝父子關(guān)系氧急,至于為什么RootBeanDefinition
依然在使用颗胡,我覺得可能是為了兼容老版本的原因吧上面說過,既然流程走到標(biāo)注3處說明此時我們使用
FactoryBean
的id/name
要獲得生產(chǎn)的bean
吩坝,此時首先從Map<String, Object> factoryBeanObjectCache
緩存中獲取毒姨,其中key
為FactoryBean name
,value
為根據(jù)FactoryBean
生產(chǎn)的對應(yīng)bean
钉寝,緩存中已經(jīng)存在直接返回弧呐,沒有繼續(xù)往下繁瑣的Spring又出現(xiàn)一個緩存mergedBeanDefinition
,key
為標(biāo)簽id/name
嵌纲,value
保存對應(yīng)RootBeanDefinition
俘枫,存在RootBeanDefinition
直接返回,但要注意點逮走,經(jīng)過上兩篇文章的分析崩哩,此時在DefaultListableBeanFactory
中的緩存beanDefinitionMap
中已經(jīng)存在beanName
和BeanDefinition
的映射了,所以圖中的getBeanDefinition(String)
實際上會返回beanName
對應(yīng)的GenericBeanDefinition
對象言沐,最后調(diào)用RootBeanDefinition getMergedBeanDefinition(String, BeanDefinition, BeanDefinition)
邓嘹,上面說過<bean>
可能存在父子關(guān)系,在該方法中如果當(dāng)前BeanDefinition
為子對象险胰,會根據(jù)parentName
得到對應(yīng)的父BeanDefinition
汹押,并通過遞歸調(diào)用的方式將父BeanDefinition
融合進子BeanDefinition
,最后包裝成RootBeanDefinition
返回起便,該方法中又是一坨惡心的邏輯棚贾,這里就不挨個細(xì)聊,有興趣的讀者可以繼續(xù)深入榆综。圖8標(biāo)注5是根據(jù)FactoryBean
名稱獲取bean
的入口
首先根據(jù)
beanName
判斷該FactoryBean
(由前面的分析可知此時beanName
只可能是FactoryBean
自定義實現(xiàn)類的id/name
)是否單例妙痹,是否存在于緩存singletonObjects
中,不滿足直接調(diào)用核心方法Object doGetObjectFromFactoryBean(FactoryBean<?>, String, boolean)
生成對應(yīng)bean
鼻疮,否則再嘗試從factoryBeanObjectCache
中獲取bean
怯伊,上面說過該緩存內(nèi)是FactoryBean name
和生成bean
的映射,如果映射不存在依然調(diào)用Object doGetObjectFromFactoryBean(FactoryBean<?>, String, boolean)
判沟,并更新factoryBeanObjectCache
耿芹,因此最后核心創(chuàng)建bean
的職責(zé)就落在了紅框內(nèi)忽略其他不相干的部分,只關(guān)注所畫代碼邏輯還是非常清楚的挪哄,直接
factory.getObject()
多態(tài)執(zhí)行我們自定義FactoryBean
的getObject()
吧秕,如果存在自定義的BeanPostProcessor
會進入標(biāo)注2處代碼,我們還是以例子作為切入點講解迹炼。寫一個自定義后處理器首先需要實現(xiàn)BeanPostProcessor
接口接口有兩個待實現(xiàn)方法砸彬,好理解的是該處理器肯定是在
bean
實例創(chuàng)建完成之后調(diào)用的,但是這里的before/after Initialization
指的是是什么呢?我們的bean
依然是Student
砂碉,但是該類實現(xiàn)了InitializingBean
的afterPropertiesSet()
方法吟秩,該方法可供創(chuàng)建對象時進行一些初始化的行為,我們分別在Student
的構(gòu)造器和afterPropertiesSet()
內(nèi)打印一句話這里擴充點知識绽淘,Spring提供了三種方式讓程序員在對象創(chuàng)建和銷毀時進行一些自定義的處理:1.
@PostConstruct
和@PreDestory
;2.標(biāo)簽中定義init-method
和destory-method
闹伪;3.實現(xiàn)InitializingBean
和DisposableBean
沪铭,即便都是初始化/銷毀方式內(nèi)部執(zhí)行的順序也是有不同的,具體的使用方式和差異請讀者自行研究偏瓤。但是上面所說的before/after Initialization
就是基于最后一種方式來說的杀怠。我們在XML上配置好自定義bean
后處理器和Student
運行一下,結(jié)果如下結(jié)果很明顯最先執(zhí)行的是
Student
的構(gòu)造器厅克,而后為自定義bean
后處理器的postProcessBeforeInitialization()
赔退,實現(xiàn)的afterPropertiesSet()
第三,最后是postProcessAfterInitialization()
证舟。這里例子中Spring創(chuàng)建了普通的bean
硕旗,如果使用FactoryBean
來生成Student
對象又會發(fā)生什么呢?答案在BeanPostProcessor
的注解中寫的很明白對于使用自定義
FactoryBean
創(chuàng)建bean
的流程來說postProcessAfterInitialization()
方法會調(diào)用兩次女责,一次是實例化自定義FactoryBean
時漆枚,第二次是FactoryBean.getObject()
創(chuàng)建bean
時。解釋完bean
后處理器的使用和觸發(fā)順序我們再來看Spring是如何調(diào)用我們自定的后處理器抵知,圖12標(biāo)注2就是其中一個調(diào)用點墙基,此時this
為DefaultListableBeanFactory
的實例,獲取所有自定后處理器并調(diào)用的方法封裝在DefaultListableBeanFactory
的父類AbstractAutowireCapableBean
中從
List<BeanPostProcessor> beanPostProcessors
中遍歷每一個自定義后處理器刷喜,依次調(diào)用postProcessAfterInitialization(Object, String)
残制,大家可能會感到奇怪,為什么只調(diào)用postProcessAfterInitialization
而沒有調(diào)用postProcessBeforeInitialization
呢掖疮,其實呢上面我們說過對于使用自定義FactoryBean
創(chuàng)建bean
流程來說postProcessAfterInitialization
會被調(diào)用兩次初茶,這里實際上就是第二次對于生產(chǎn)出來的bean
做的后處理,而第一次調(diào)用在哪浊闪?postProcessBeforeInitialization
的調(diào)用在哪纺蛆?答案是在new ClassPathXmlApplicationContext(String[])
中隱式調(diào)用過了,也就是文章開始說過的“下半部分”處進行了調(diào)用规揪,如果大家還懵逼的話桥氏,下一篇分析”下半部分“就清楚了