SpringBoot啟動原理解析

概述

原文鏈接:http://tengj.top/2017/03/09/springboot3/

前面幾章我們見識了SpringBoot為我們做的自動配置乃戈,確實(shí)方便快捷蜓谋,但是對于新手來說鸟辅,如果不大懂SpringBoot內(nèi)部啟動原理扣讼,以后難免會吃虧捶闸。
本次我們一起一步步揭開SpringBoot的神秘面紗胯府,讓它不在神秘秋秤。

啟動原理解析

我們開發(fā)任何一個Spring Boot項(xiàng)目宏粤,都會用到如下的啟動類:

@SpringBootApplication
public class BootDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootDemoApplication.class, args);
    }
}

從上面代碼可以看出,Annotation定義(@SpringBootApplication)和類定義(SpringApplication.run)最為耀眼灼卢,所以要揭開SpringBoot的神秘面紗绍哎,我們要從這兩位開始就可以了。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
  ...
}

雖然定義使用了多個Annotation進(jìn)行了原信息標(biāo)注鞋真,但實(shí)際上重要的只有三個Annotation:

  • @Configuration(@SpringBootConfiguration點(diǎn)開查看發(fā)現(xiàn)里面還是應(yīng)用了@Configuration)
  • @EnableAutoConfiguration
  • @ComponentScan

所以崇堰,如果我們使用如下的SpringBoot啟動類,整個SpringBoot應(yīng)用依然可以與之前的啟動類功能對等:

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class BootDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootDemoApplication.class, args);
    }
}

每次寫這3個比較累涩咖,所以寫一個@SpringBootApplication方便點(diǎn)海诲。接下來分別介紹這3個Annotation

@Configuration

這里的@Configuration對我們來說不陌生,它就是JavaConfig形式的Spring Ioc容器的配置類使用的那個@Configuration檩互,SpringBoot社區(qū)推薦使用基于JavaConfig的配置形式特幔,所以,這里的啟動類標(biāo)注了@Configuration之后闸昨,本身其實(shí)也是一個IoC容器的配置類蚯斯。

@ComponentScan

@ComponentScan這個注解在Spring中很重要,其功能其實(shí)就是自動掃描并加載符合條件的組件(比如@Component和@Repository等)或者bean定義零院,最終將這些bean定義加載到IoC容器中溉跃。
我們可以通過basePackages等屬性來細(xì)粒度的定制@ComponentScan自動掃描的范圍村刨,如果不指定告抄,則默認(rèn)Spring框架實(shí)現(xiàn)會從聲明@ComponentScan所在類的package進(jìn)行掃描。
注:所以SpringBoot的啟動類最好是放在root package下嵌牺,因?yàn)槟J(rèn)不指定basePackages

@ EnableAutoConfiguration

個人感覺@EnableAutoConfiguration這個Annotation最為重要打洼,所以放在最后來解讀。
大家是否還記得Spring框架提供的各種名字為@Enable開頭的Annotation定義逆粹?比如@EnableScheduling募疮、@EnableCaching、@EnableMBeanExport等僻弹。
@EnableAutoConfiguration的理念和做事方式其實(shí)一脈相承阿浓,簡單概括一下就是,借助@Import的支持蹋绽,收集和注冊特定場景相關(guān)的bean定義芭毙。

  • @EnableScheduling是通過@Import將Spring調(diào)度框架相關(guān)的bean定義都加載到IoC容器
  • @EnableMBeanExport是通過@Import將JMX相關(guān)的bean定義加載到IoC容器

而@EnableAutoConfiguration也是借助@Import的幫助筋蓖,將所有符合自動配置條件的bean定義加載到IoC容器,僅此而已退敦!
@EnableAutoConfiguration作為一個復(fù)合Annotation,其自身定義關(guān)鍵信息如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
  ...
}

其中粘咖,最關(guān)鍵的要屬@Import(AutoConfigurationImportSelector.class),借助AutoConfigurationImportSelector侈百,@EnableAutoConfiguration可以幫助SpringBoot應(yīng)用將所有符合條件的@Configuration配置都加載到當(dāng)前SpringBoot創(chuàng)建并使用的IoC容器瓮下,就像一只“八爪魚”一樣!

/**
 * Return the auto-configuration class names that should be considered. By default
 * this method will load candidates using {@link SpringFactoriesLoader} with
 * {@link #getSpringFactoriesLoaderFactoryClass()}.
 * @param metadata the source metadata
 * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
 * attributes}
 * @return a list of candidate configurations
 */
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
            + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}

/**
 * Return the class used by {@link SpringFactoriesLoader} to load configuration
 * candidates.
 * @return the factory class
 */
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

借助于Spring框架原有的一個工具類:SpringFactoriesLoader的支持钝域,@EnableAutoConfiguration可以智能的自動配置功效才得以大功告成


自動配置幕后英雄:SpringFactoriesLoader詳解

SpringFactoriesLoader屬于Spring框架私有的一種擴(kuò)展方案讽坏,其主要功能就是從指定的配置文件META-INF/spring.factories加載配置。
配合@EnableAutoConfiguration使用的話例证,它更多是提供一種配置查找的功能支持震缭,即根據(jù)@EnableAutoConfiguration的完整類名org.springframework.boot.autoconfigure.EnableAutoConfiguration作為查找的Key,獲取對應(yīng)的一組@Configuration類

上圖就是從SpringBoot的autoconfigure依賴包中的META-INF/spring.factories配置文件中摘錄的一段內(nèi)容战虏,可以很好地說明問題拣宰。
所以,@EnableAutoConfiguration自動配置的魔法騎士就變成了:從classpath中搜尋所有的META-INF/spring.factories配置文件烦感,并將其中org.springframework.boot.autoconfigure.EnableutoConfiguration對應(yīng)的配置項(xiàng)通過反射(Java Refletion)實(shí)例化為對應(yīng)的標(biāo)注了@Configuration的JavaConfig形式的IoC容器配置類巡社,然后匯總為一個并加載到IoC容器。

深入探索SpringApplication執(zhí)行流程

1)如果我們使用的是SpringApplication的靜態(tài)run方法手趣,那么晌该,這個方法里面首先要創(chuàng)建一個SpringApplication對象實(shí)例,然后調(diào)用這個創(chuàng)建好的SpringApplication的實(shí)例方法绿渣。在SpringApplication實(shí)例初始化的時候朝群,它會提前做幾件事情:

  • 根據(jù)classpath里面是否存在某個特征類(org.springframework.web.context.ConfigurableWebApplicationContext)來決定是否應(yīng)該創(chuàng)建一個為Web應(yīng)用使用的ApplicationContext類型
  • 使用SpringFactoriesLoader在應(yīng)用的classpath中查找并加載所有可用的ApplicationContextInitializer
  • 使用SpringFactoriesLoader在應(yīng)用的classpath中查找并加載所有可用的ApplicationListener
  • 推斷并設(shè)置main方法的定義類

2)SpringApplication實(shí)例初始化完成并且完成設(shè)置后,就開始執(zhí)行run方法的邏輯了中符,方法執(zhí)行伊始姜胖,首先遍歷執(zhí)行所有通過SpringFactoriesLoader可以查找到并加載的SpringApplicationRunListener。調(diào)用它們的started()方法淀散,告訴這些SpringApplicationRunListener右莱,“嘿,SpringBoot應(yīng)用要開始執(zhí)行咯档插!”慢蜓。

3)創(chuàng)建并配置當(dāng)前Spring Boot應(yīng)用將要使用的Environment(包括配置要使用的PropertySource以及Profile)。

4)遍歷調(diào)用所有SpringApplicationRunListener的environmentPrepared()的方法郭膛,告訴他們:“當(dāng)前SpringBoot應(yīng)用使用的Environment準(zhǔn)備好了咯晨抡!”。

5)如果SpringApplication的showBanner屬性被設(shè)置為true,則打印banner耘柱。

6)根據(jù)用戶是否明確設(shè)置了applicationContextClass類型以及初始化階段的推斷結(jié)果圆雁,決定該為當(dāng)前SpringBoot應(yīng)用創(chuàng)建什么類型的ApplicationContext并創(chuàng)建完成,然后根據(jù)條件決定是否添加ShutdownHook帆谍,決定是否使用自定義的BeanNameGenerator伪朽,決定是否使用自定義的ResourceLoader,當(dāng)然汛蝙,最重要的烈涮,將之前準(zhǔn)備好的Environment設(shè)置給創(chuàng)建好的ApplicationContext使用。

7)ApplicationContext創(chuàng)建好之后窖剑,SpringApplication會再次借助Spring-FactoriesLoader坚洽,查找并加載classpath中所有可用的ApplicationContext-Initializer,然后遍歷調(diào)用這些ApplicationContextInitializer的initialize(applicationContext)方法來對已經(jīng)創(chuàng)建好的ApplicationContext進(jìn)行進(jìn)一步的處理西土。

8)遍歷調(diào)用所有SpringApplicationRunListener的contextPrepared()方法讶舰。

9)最核心的一步,將之前通過@EnableAutoConfiguration獲取的所有配置以及其他形式的IoC容器配置加載到已經(jīng)準(zhǔn)備完畢的ApplicationContext需了。

10)遍歷調(diào)用所有SpringApplicationRunListener的contextLoaded()方法跳昼。

11)調(diào)用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序肋乍。

12)查找當(dāng)前ApplicationContext中是否注冊有CommandLineRunner鹅颊,如果有,則遍歷執(zhí)行它們墓造。

13)正常情況下,遍歷執(zhí)行SpringApplicationRunListener的finished()方法觅闽、(如果整個過程出現(xiàn)異常帝雇,則依然調(diào)用所有SpringApplicationRunListener的finished()方法,只不過這種情況下會將異常信息一并傳入處理)

去除事件通知點(diǎn)后蛉拙,整個流程如下:

總結(jié)

到此野来,SpringBoot的核心組件完成了基本的解析豁辉,綜合來看碳锈,大部分都是Spring框架背后的一些概念和實(shí)踐方式欺抗,SpringBoot只是在這些概念和實(shí)踐上對特定的場景事先進(jìn)行了固化和升華绞呈,而也恰恰是這些固化讓我們開發(fā)基于Sping框架的應(yīng)用更加方便高效贸人。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市佃声,隨后出現(xiàn)的幾起案子灸姊,更是在濱河造成了極大的恐慌,老刑警劉巖秉溉,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件力惯,死亡現(xiàn)場離奇詭異,居然都是意外死亡召嘶,警方通過查閱死者的電腦和手機(jī)父晶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弄跌,“玉大人甲喝,你說我怎么就攤上這事☆踔唬” “怎么了埠胖?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長淳玩。 經(jīng)常有香客問我直撤,道長,這世上最難降的妖魔是什么蜕着? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任谋竖,我火速辦了婚禮红柱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蓖乘。我一直安慰自己锤悄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布嘉抒。 她就那樣靜靜地躺著零聚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪些侍。 梳的紋絲不亂的頭發(fā)上隶症,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機(jī)與錄音娩梨,去河邊找鬼沿腰。 笑死,一個胖子當(dāng)著我的面吹牛狈定,可吹牛的內(nèi)容都是我干的颂龙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼纽什,長吁一口氣:“原來是場噩夢啊……” “哼措嵌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起芦缰,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤企巢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后让蕾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浪规,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年探孝,在試婚紗的時候發(fā)現(xiàn)自己被綠了笋婿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡顿颅,死狀恐怖缸濒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情粱腻,我是刑警寧澤庇配,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站绍些,受9級特大地震影響捞慌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜遇革,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一卿闹、第九天 我趴在偏房一處隱蔽的房頂上張望揭糕。 院中可真熱鬧萝快,春花似錦锻霎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至奄容,卻和暖如春冰更,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背昂勒。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工蜀细, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人戈盈。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓奠衔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親塘娶。 傳聞我的和親對象是個殘疾皇子归斤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354