spring源碼解析之SpringIOC源碼解析(上)

SpringIOC源碼解析(上)

一、什么是SpringIOC

spring ioc指的是控制反轉(zhuǎn)懈糯,IOC容器負(fù)責(zé)實例化涤妒、定位、配置應(yīng)用程序中的對象及建立這些對象間的依賴昂利。交由Spring容器統(tǒng)一進行管理届腐,從而實現(xiàn)松耦合。

二蜂奸、SpringIOC源碼解析

在開始之前我們先通過一個簡單的示意圖來了解一下大概的一個流程

?

從示意圖可以看出犁苏,當(dāng)web容器啟動的時候,spring的全局bean的管理器會去xml配置文件中掃描的包下面獲取到所有的類扩所,并根據(jù)你使用的注解围详,進行對應(yīng)的封裝,封裝到全局的bean容器中進行管理祖屏,一旦容器初始化完畢助赞,beanID以及bean實例化的類對象信息就全部存在了袁勺,現(xiàn)在我們需要在某個service里面調(diào)用另一個bean的某個方法的時候雹食,我們只需要依賴注入進來另一個bean的Id即可,調(diào)用的時候期丰,spring會去初始化完成的bean容器中獲取即可群叶,如果存在就把依賴的bean的類的實例化對象返回給你吃挑,你就可以調(diào)用依賴的bean的相關(guān)方法或?qū)傩缘龋?/p>

本文會分析Spring的IOC模塊的整體流程,分析過程需要使用一個簡單的demo工程來啟動Spring街立,需要的童鞋可自行下載

https://github.com/cjinjun/spring-framework-demo

工程示例代碼

寫一個簡單的接口和接口實現(xiàn)類

public interface IOCService {

? ? String helloIoc();

}

public class IOCServiceImpl implements IOCService {

? ? @Override

? ? public String helloIoc() {

? ? ? ? return "Hello,IOC";

? ? }

}

新建一個application-ioc.xml

<?xml version="1.0" encoding="UTF-8" ?>

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

? ? ? xmlns="http://www.springframework.org/schema/beans"

? ? ? xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byName">

? ? <bean id="iocService" class="com.jinjun.demo.ioc.service.impl.IOCServiceImpl"/>

</beans>

啟動spring

public class IOCDemo {

public static void main (String args[]){

ApplicationContext context =new ClassPathXmlApplicationContext("classpath:application-ioc.xml");

? ? ? ? IOCService iocService=context.getBean(IOCService.class);

? ? ? ? System.out.println(iocService.helloIoc());

? ? }

}

ClassPathXmlApplicationContext

在看具體源碼之前先看一下這兄弟的類圖

?

最下邊ClassPathXmlApplicationContext就是咱們今天要擼的舶衬、分析的主角。

看一下這個類的源碼

?

里邊的方法基本上都是重載方法赎离,整體來看源碼也比較簡單只有setConfigLocations和refresh兩個方法沒有看到具體的實現(xiàn)逛犹。但是如果你因為這個而小瞧了Spring那可就大錯特錯了,setConfigLocations只是一個開胃小菜梁剔,refresh才是核心重點虽画。

setConfigLocations方法的主要工作有兩個:創(chuàng)建環(huán)境對象ConfigurableEnvironment和處理ClassPathXmlApplicationContext傳入的字符串中的占位符

?

?

?

這里的getEnironment就涉及到創(chuàng)建環(huán)境變量的操作。

這個接口ConfigurableEnvironment比較重要的就是兩部分內(nèi)容了憾朴,一個是設(shè)置Spring的環(huán)境就是我們經(jīng)常用的spring.profile配置狸捕。另外就是系統(tǒng)資源Property。

接著看`createEnvironment()`方法众雷,發(fā)現(xiàn)它返回了一個`StandardEnvironment`類灸拍,而這個類中的`customizePropertySources`方法就會往資源列表中添加Java進程中的變量和系統(tǒng)的環(huán)境變量

?

?

處理占位符

再次回到 `resolvePath`方法后跟進通過上方獲取的`ConfigurableEnvironment`接口的`resolveRequiredPlaceholders`方法,終點就是下方的這個方法砾省。這個方法主要就是處理所有使用${}方式的占位符

開胃小菜已經(jīng)說完了鸡岗,現(xiàn)在才是本文重中之重refresh

refresh

方法內(nèi)容比較多,以下將用較長的篇幅來解析說明這個方法编兄,童鞋們可以提前做好心里準(zhǔn)備轩性。

先看一下refresh方法里邊內(nèi)容

public void refresh()throws BeansException, IllegalStateException {

synchronized (this.startupShutdownMonitor) {

// Prepare this context for refreshing.

? ? ? prepareRefresh();

? ? ? // Tell the subclass to refresh the internal bean factory.

? ? ? ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

? ? ? // Prepare the bean factory for use in this context.

? ? ? prepareBeanFactory(beanFactory);

? ? ? try {

// Allows post-processing of the bean factory in context subclasses.

? ? ? ? postProcessBeanFactory(beanFactory);

? ? ? ? // Invoke factory processors registered as beans in the context.

? ? ? ? invokeBeanFactoryPostProcessors(beanFactory);

? ? ? ? // Register bean processors that intercept bean creation.

? ? ? ? registerBeanPostProcessors(beanFactory);

? ? ? ? // Initialize message source for this context.

? ? ? ? initMessageSource();

? ? ? ? // Initialize event multicaster for this context.

? ? ? ? initApplicationEventMulticaster();

? ? ? ? // Initialize other special beans in specific context subclasses.

? ? ? ? onRefresh();

? ? ? ? // Check for listener beans and register them.

? ? ? ? registerListeners();

? ? ? ? // Instantiate all remaining (non-lazy-init) singletons.

? ? ? ? finishBeanFactoryInitialization(beanFactory);

? ? ? ? // Last step: publish corresponding event.

? ? ? ? finishRefresh();

? ? ? }

catch (BeansException ex) {

if (logger.isWarnEnabled()) {

logger.warn("Exception encountered during context initialization - " +

"cancelling refresh attempt: " + ex);

? ? ? ? }

// Destroy already created singletons to avoid dangling resources.

? ? ? ? destroyBeans();

? ? ? ? // Reset 'active' flag.

? ? ? ? cancelRefresh(ex);

? ? ? ? // Propagate exception to caller.

? ? ? ? throw ex;

? ? ? }

finally {

// Reset common introspection caches in Spring's core, since we

// might not ever need metadata for singleton beans anymore...

? ? ? ? resetCommonCaches();

? ? ? }

}

}

看著東西是不是比較多,容易懵狠鸳?不要慌揣苏,咱們一步一步分析。與它死磕到底件舵,不研究明白不罷休卸察。

第一行 synchronized 這個關(guān)鍵字作用我就不多說了把,如果想深入了解可以自行百度铅祸,在這里為了避免`refresh()` 還沒結(jié)束坑质,再次發(fā)起啟動或者銷毀容器引起的沖突

`prepareRefresh()` 從字面意思來看說 “預(yù)準(zhǔn)備refresh" 主要做一些準(zhǔn)備工作,記錄容器的啟動時間临梗、標(biāo)記“已啟動”狀態(tài)涡扼、檢查環(huán)境變量等

`obtainFreshBeanFactory()` 乍一看這個方法也沒幾行代碼,但是這個方法在執(zhí)行refreshBeanFactory時候創(chuàng)建一個默認(rèn)的BeanFactory:DefaultListableBeanFactory盟庞,并加載BeanDefinition吃沪。

prepareBeanFactory(beanFactory),為beanFactory設(shè)置一些屬性如ClassLoader,BeanExpressionResolver,PropertyEditorRegistrar,BeanPostProcessor等

postProcessBeanFactory()這個比較簡單什猖,是Spring的一個擴展點,如果有Bean實現(xiàn)了BeanFactoryPostProcessor接口巷波,那么在容器初始化以后萎津,Spring 會負(fù)責(zé)調(diào)用里面的 postProcessBeanFactory 方法

invokeBeanFactoryPostProcessors(beanFactory),為beanFactory注冊BeanFactoryPostProcessor

registerBeanPostProcessors(beanFactory)抹镊,注冊當(dāng)Bean創(chuàng)建時候的BeanPostProcessor

initMessageSource()初始化上下文的消息源:DelegatingMessageSource

initApplicationEventMulticaster()初始化了一個事件傳播器:SimpleApplicationEventMulticaster

`onRefresh()` 又是一個擴展點,子類可以在這里來做一些想做的事情

registerListeners()獲取ApplicationListener荤傲,并在事件傳播器中注冊他們

finishBeanFactoryInitialization(beanFactory)垮耳,獲取LoadTimeWeaverAware并初始化他們,初始化單例并且非懶加載的Bean

finishRefresh()完成refresh Context操作遂黍,初始化LifecycleProcessor并start终佛,發(fā)布ContextRefreshedEvent事件

resetCommonCaches()主要是清理緩存

一個ClassPathXmlApplicationContext的初始化過程基本如上,詳細(xì)的BeanDefinition加載過程雾家,獲取Bean操作期待下篇铃彰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市芯咧,隨后出現(xiàn)的幾起案子牙捉,更是在濱河造成了極大的恐慌,老刑警劉巖敬飒,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邪铲,死亡現(xiàn)場離奇詭異,居然都是意外死亡无拗,警方通過查閱死者的電腦和手機带到,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來英染,“玉大人揽惹,你說我怎么就攤上這事∷目担” “怎么了搪搏?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長箭养。 經(jīng)常有香客問我慕嚷,道長,這世上最難降的妖魔是什么毕泌? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任喝检,我火速辦了婚禮,結(jié)果婚禮上撼泛,老公的妹妹穿的比我還像新娘挠说。我一直安慰自己,他們只是感情好愿题,可當(dāng)我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布损俭。 她就那樣靜靜地躺著蛙奖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪杆兵。 梳的紋絲不亂的頭發(fā)上雁仲,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天,我揣著相機與錄音琐脏,去河邊找鬼攒砖。 笑死,一個胖子當(dāng)著我的面吹牛日裙,可吹牛的內(nèi)容都是我干的吹艇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼昂拂,長吁一口氣:“原來是場噩夢啊……” “哼受神!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起格侯,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤鼻听,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后养交,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體精算,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年碎连,在試婚紗的時候發(fā)現(xiàn)自己被綠了灰羽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡鱼辙,死狀恐怖廉嚼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情倒戏,我是刑警寧澤怠噪,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站杜跷,受9級特大地震影響傍念,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜葛闷,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一憋槐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧淑趾,春花似錦阳仔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嘶摊。三九已至,卻和暖如春评矩,著一層夾襖步出監(jiān)牢的瞬間叶堆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工稚照, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蹂空,地道東北人。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓果录,卻偏偏與公主長得像,于是被迫代替她去往敵國和親咐熙。 傳聞我的和親對象是個殘疾皇子弱恒,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,647評論 2 354

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