## Spring-Core核心邏輯源碼分析

<a></a>初始化容器

  1. 服務(wù)器容器根據(jù)配置文件(Tomcat:web.xml)加載ContextInitialListener事件:ContextLoaderListener.contextInitialized();
  2. 嘗試獲得parentContext;
  3. 根據(jù)parentContext [初始化ApplicationContext](#Initial ApplicationContext);
  4. ApplicationRoot為key对人,將context注冊(cè)到servletContext中;
  5. 如果當(dāng)前線程的ClassLoader為ContextClassLoader,則將context設(shè)置為currentContext;否則將context設(shè)置為perThreadContext;

<a name='Initial ApplicationContext'>初始化ApplicationContext</a>

  1. 通過(guò)BeanUtil.instantiateClass()來(lái)創(chuàng)建ConfigurableWebApplicationContext曲饱;
  2. 設(shè)置context的id洲炊、parent梅割、servletContext等屬性(不曉得Id有什么用乃坤,代碼還費(fèi)了好幾行獲得best possible id??);
  3. 調(diào)用ctx.refresh()方法[構(gòu)建Spring容器](#start up Spring)構(gòu)建Spring容器.

通過(guò)refresh方式初始化容器而不是init的方式陶贼,而且refresh中有通過(guò)beanFactory.getBean方式獲取的對(duì)象悦荒,初始化階段BeanFactory中是沒(méi)有BeanDefinition的春贸,因此這里我懷疑和tomcat的熱部署有關(guān)系混萝。

<a name='start up Spring'>構(gòu)建Spring容器</a>

  1. 根據(jù)信號(hào)startupShutdownMonitor來(lái)作同步,在同步代碼塊中進(jìn)行refresh操作;

Spring注釋對(duì)startupShutdownMonitor有如下解釋:
Synchronization monitor for the "refresh" and "destroy".
即destroy方法必須等refresh方法執(zhí)行完之后才能被調(diào)用;

  1. prepareRefresh():準(zhǔn)備刷新萍恕,這里有個(gè)同步設(shè)置變量active逸嘀,沒(méi)看懂啥意思;
  2. obtainRefreshBeanFactory():[初始化](#init beanFactory)并獲取BeanFactory;
  3. prepareBeanFactory():初始化BeanFactory的依賴允粤,包括ignoreDependency和硬代碼registerDependency;Ignore掉的依賴通常是由別的方式進(jìn)行注入崭倘,比如BeanFactory對(duì)象可以由BeanFactoryAware方式注入;獲取并維護(hù)系統(tǒng)變量和環(huán)境變量
  4. invokeBeanFactoryPostProcessor():根據(jù)BeanFactory是否為BeanDefinitionRegistry來(lái)調(diào)用BeanFactoryPostProcessor,再根據(jù)priority类垫、order司光、nonOrder的順序調(diào)用BeanPostProcessor。在服務(wù)器初始化階段并沒(méi)有注冊(cè)BeanFactoryPostProcessor悉患,也許在熱部署階段會(huì)執(zhí)行吧残家;也就是說(shuō),BeanFactoryPostProcessor是在BeanFactory初始化之后Bean對(duì)象初始化前執(zhí)行的
  5. registerBeanPostProcessor():根據(jù)BeanFactory.getBeanNamesForType()獲取BeanPostProcessor售躁,然后以Priority坞淮、Order茴晋、NonOrder的順序注冊(cè)到BeanPostProcessorList中。BeanPostProcessorList為ArrayList回窘,因此執(zhí)行順序?yàn)椋篜riority>Order>NonOrder诺擅;
  6. initMessageSource():獲取MessageSource對(duì)象,默認(rèn)為DelegatingMessageSource;
  7. 接下來(lái)是注冊(cè)廣播事件監(jiān)聽(tīng)器啡直、調(diào)用子類的onRefresh方法烁涌、注冊(cè)應(yīng)用監(jiān)聽(tīng)器;
  8. finishBeanFactoryInitialization():初始化所有not-lazy-init Bean酒觅。也是SpringCore的核心代碼烹玉;
  9. finishRefresh:觸發(fā)加載完成事件;

<a name='init beanFactory'>初始化BeanFactory</a>

  1. 清空已加載的Bean阐滩;
  2. 創(chuàng)建一個(gè)內(nèi)部BeanFactory;
  3. 自定義BeanFactory配置:setAllowBeanDefinitionOverriding县忌、setAllowEagerClassLoading掂榔、setAllowCircularReferences、setAllowRawInjectionDespiteWrapping
  4. loadBeanDefinitions():遍歷Resources中的Xml定義的bean加載至BeanDefinition症杏;

<a name='loadBeanDefinition'>Bean加載</a>

  1. 根據(jù)Resource生成Document對(duì)象装获;根據(jù)Document的Element的nodeName屬性來(lái)處理Bean:
    • 如果nodeName為Import,則執(zhí)行importBeanDefinitionResource操作加載import-resource定義的bean:
    • 如果nodeName為Alias厉颤,則將BeanName與Alias關(guān)聯(lián)注冊(cè)穴豫;
    • 如果nodeName為Bean,則根據(jù)屬性解析Element生成BeanHolder逼友,將BeanName與BeanDefinition注冊(cè)到BeanDefinitions緩存中;
    • 如果nodeName為Beans,則跳至1.處理Beans中的Bean精肃;

<a name='spring-core'>核心代碼</a>

  1. 1.finishBeanFactoryInitialization->preInstantiateSingletons():同步獲取緩存中的beanDefinitionNames,放到局部變量中;
  2. 遍歷beanDefinitionName,調(diào)用[getBean()](#get bean)初始化Bean;
<a name = 'get bean'>getBean()</a>
  1. 首先嘗試從singletonObjects帜乞、earlySingletonObjects的緩存中獲取對(duì)象;
  2. 如果對(duì)象不為空司抱,則調(diào)用getObjectForBeanInstance()方法通過(guò)FactoryBean或者對(duì)象本身的方式獲得Bean對(duì)象;
  3. 如果對(duì)象不為空,則嘗試在ParentBeanFactory中獲得對(duì)象黎烈,并返回习柠;
  4. 如果ParentBeanFactory對(duì)象中沒(méi)有獲得Bean對(duì)象,則讀取BeanDefinistions緩存中獲得該Bean的Definition對(duì)象照棋;
  5. 拿到BeanDefinition對(duì)象后资溃,遍歷該對(duì)象的dependsOn屬性,并將該BeanName與DependsOn的對(duì)象Name注冊(cè)dependentBeanMap中烈炭,然后調(diào)用[getBean()](#get bean)來(lái)實(shí)例化DependsOn的對(duì)象溶锭。這里會(huì)根據(jù)dependentBeanMap來(lái)檢查是否存在循環(huán)依賴,(Spring對(duì)Bean屬性之間的循壞依賴是有解決方案的梳庆,不清楚這里的循壞依賴為什么會(huì)直接拋出異常)暖途;
  6. 實(shí)例化DependsOn對(duì)象后卑惜,再根據(jù)Bean的Scope屬性創(chuàng)建對(duì)象,Singleton方式和prototype方式的區(qū)別在于Singleton對(duì)象在創(chuàng)建的時(shí)候首先會(huì)在緩存中取對(duì)象驻售,如果不存在再createBean()創(chuàng)建一個(gè)新的對(duì)象放到singletonObjects和earlySingletonObjects中露久,而prototype類型的則不不會(huì)在緩存中取對(duì)象,直接創(chuàng)建新對(duì)象.
  7. getObjectForBeanInstance()
    :拿到Bean對(duì)象之后欺栗,確定Bean是否為FactoryBean毫痕,如果是則需要通過(guò)FactoryBean的方式獲取bean,否則直接返回bean.
<a name='createBean'>createBean()</a>
  1. 在初始化前迟几,首先調(diào)用resolveBeanClass()確保beanClass已經(jīng)加載到ClassLoader里面消请;這里會(huì)去看BeanDefinition中是否有指定的class,如果沒(méi)有类腮,則會(huì)調(diào)用ClassUtils.forName()在ClassLoader容器中加載class臊泰;
  2. 確保class已經(jīng)加載完成之后,執(zhí)行resolveBeforeInstantiation()蚜枢,嘗試通過(guò)InstantiationAwareBeanPostProcessor來(lái)創(chuàng)建Bean缸逃,如果Bean創(chuàng)建成功,則調(diào)用applyBeanPostProcessorsAfterInitialization()應(yīng)用Bean后處理器厂抽,并返回Bean對(duì)象需频;(InstantiationAwareBeanPostProcessor這個(gè)里面雖然可以返回null,但是返回null之后筷凤,Spring容器會(huì)認(rèn)為沒(méi)有創(chuàng)建bean昭殉,重新創(chuàng)建bean)
  3. 如果InstantiationAwareBeanPostProcessor返回null,調(diào)用doCreateBean()真正的創(chuàng)建Bean對(duì)象.(os:創(chuàng)建個(gè)對(duì)象前戲真長(zhǎng)??)
<a name='doCreateBean'>doCreateBean()</a>
  1. 通過(guò)createBeanInstance()創(chuàng)建Bean實(shí)例。該方法首先在resolvedConstructor的一系列變量中找到緩存的Constructor藐守,否則找到一個(gè)FastConstructor(找最合適的過(guò)程比較冗長(zhǎng)挪丢,這里省略這么多字),再根據(jù)是否有OverrideMethod吗伤,來(lái)選擇通過(guò)Reflect的形式還是CGlib的形式創(chuàng)建對(duì)象吃靠;
  2. Bean實(shí)例化出來(lái)之后,調(diào)用applyMergedBeanDefinitionPostProcessors()來(lái)執(zhí)行BeanPostProcessor足淆;
  3. 這時(shí)候?qū)傩赃€是空的巢块,在注入屬性時(shí)會(huì)發(fā)生循環(huán)依賴的問(wèn)題,因此這里會(huì)判斷是否允許循環(huán)依賴巧号,如果允許的話將bean的引用通過(guò)ObejctFactory工廠的方式暴露出去族奢;
  4. populateBean(),遍歷并且注入依賴丹鸿。
  5. initializeBean()越走,調(diào)用Bean的初始化方法,init啦,afterPropertiesSet啦廊敌。


額外補(bǔ)充:實(shí)例Bean是什么時(shí)候變成Proxy的呢铜跑?

通過(guò)doCreateBean()的步驟1.出來(lái)的實(shí)例這個(gè)時(shí)候還是對(duì)象本身,而Spring暴露出來(lái)的Bean又都是以代理形式出現(xiàn)的骡澈,那么Spring是什么時(shí)候?qū)?shí)例進(jìn)行代理wrap的呢锅纺,通過(guò)Debug看到在applyMergedBeanDefinitionPostProcessors()階段,
AbstractAutoProxyCreator以BeanPostProcessor的方式對(duì)實(shí)例出來(lái)的Bean進(jìn)行AutoProxy肋殴,通過(guò)判斷該實(shí)例的Class對(duì)象是否有Interfaces囤锉,如果有則通過(guò)JDK Dynamic的方式代理,否則使用CGlib.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末护锤,一起剝皮案震驚了整個(gè)濱河市官地,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌烙懦,老刑警劉巖驱入,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異氯析,居然都是意外死亡沧侥,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門魄鸦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人癣朗,你說(shuō)我怎么就攤上這事拾因。” “怎么了旷余?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵绢记,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我正卧,道長(zhǎng)蠢熄,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任炉旷,我火速辦了婚禮签孔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘窘行。我一直安慰自己饥追,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布罐盔。 她就那樣靜靜地躺著但绕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上捏顺,一...
    開(kāi)封第一講書(shū)人閱讀 52,682評(píng)論 1 312
  • 那天六孵,我揣著相機(jī)與錄音,去河邊找鬼幅骄。 笑死劫窒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的昌执。 我是一名探鬼主播烛亦,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼懂拾!你這毒婦竟也來(lái)了煤禽?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤岖赋,失蹤者是張志新(化名)和其女友劉穎檬果,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體唐断,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡选脊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了脸甘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恳啥。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖丹诀,靈堂內(nèi)的尸體忽然破棺而出钝的,到底是詐尸還是另有隱情,我是刑警寧澤铆遭,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布硝桩,位于F島的核電站,受9級(jí)特大地震影響枚荣,放射性物質(zhì)發(fā)生泄漏碗脊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一橄妆、第九天 我趴在偏房一處隱蔽的房頂上張望衙伶。 院中可真熱鬧,春花似錦害碾、人聲如沸痕支。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)卧须。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間花嘶,已是汗流浹背笋籽。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留椭员,地道東北人车海。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像隘击,于是被迫代替她去往敵國(guó)和親侍芝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容