springboot源碼走讀之一

前面的話

首先一個(gè)最簡單的springboot的例子引入本主題捷雕。

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

其中包依賴也是最簡單的。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.spring.study</groupId>
    <artifactId>spring-study</artifactId>
    <version>1.0-SNAPSHOT</version>

<dependencies>
    <!--<dependency>-->
        <!--<groupId>org.springframework.boot</groupId>-->
        <!--<artifactId>spring-boot-starter</artifactId>-->
        <!--<version>2.2.1.RELEASE</version>-->
    <!--</dependency>-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.2.1.RELEASE</version>
    </dependency>
</dependencies>
</project>

這里就使用springboot的web方式壹甥,引入當(dāng)前最新版本2.2.1.
直接指向main方法救巷,就可以啟動(dòng)一個(gè)簡單的springboot,端口使用默認(rèn)的8080句柠。
例子有了浦译,就可以直接debug代碼棒假,走讀之。

springboot啟動(dòng)過程代碼走讀

run方法的實(shí)現(xiàn)代碼如下:

/**
     * Static helper that can be used to run a {@link SpringApplication} from the
     * specified sources using default settings and user supplied arguments.
     * @param primarySources the primary sources to load
     * @param args the application arguments (usually passed from a Java main method)
     * @return the running {@link ApplicationContext}
     */
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return new SpringApplication(primarySources).run(args);
    }

接下來看如何初始化SpringApplication

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }

從代碼可以看出精盅,這里主要做了如下幾件事情:

  1. 確定當(dāng)前應(yīng)用類型帽哑。deduceFromClasspath .springboot中主要有三種應(yīng)用類型,分別是 REACTIVE,SERVLET,NONE(普通類型)叹俏,其中SERVLET是默認(rèn)類型妻枕。
  2. 通過SPI方式,讀取spring.factories粘驰,設(shè)置應(yīng)用上下文初始化類和ApplicationListener屡谐。
  3. 確定程序的入口類

以上,就把SpringApplication的實(shí)例創(chuàng)建出來了蝌数。
接下來就是看run方法了愕掏。
先上代碼:

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);
            context = createApplicationContext();
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            listeners.started(context);
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

這里主要做了如下幾件事情:

  1. 初始化并啟動(dòng)一個(gè)計(jì)時(shí)器
  2. 設(shè)置Headless參數(shù)
  3. 通過spi方式初始化springboot啟動(dòng)過程監(jiān)聽。
  4. listeners.starting(); 發(fā)送一個(gè)ApplicationStartingEvent這個(gè)類型的事件籽前。該事件的消費(fèi)者在springboot中有兩個(gè)亭珍,分別是BackgroundPreinitializerLoggingApplicationListener. BackgroundPreinitializer是springboot異步初始化一些相對耗時(shí)的操作敷钾,減少啟動(dòng)時(shí)間枝哄,比如各種類型轉(zhuǎn)換器初始化,校驗(yàn)器初始化阻荒,http消息轉(zhuǎn)換器初始化挠锥,Jackson轉(zhuǎn)換器初始化等。LoggingApplicationListener主要是初始化日志系統(tǒng)侨赡。
  5. 裝配參數(shù)和準(zhǔn)備上下文環(huán)境及打印banner圖蓖租,即我們經(jīng)常見到的springboot的logo圖。
  6. 初始化applicationContext羊壹,當(dāng)我們啟動(dòng)的是一個(gè)SERVLET容器時(shí)蓖宦,則applicationContext為org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext,當(dāng)啟動(dòng)的是一個(gè)reactive容器,則初始化為org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext油猫,都不是稠茂,則默認(rèn)為org.springframework.context.annotation.AnnotationConfigApplicationContext
  7. 初始化異常上報(bào)類情妖。也是通過spi機(jī)制實(shí)現(xiàn)的睬关。
  8. prepareContext 主要是做一些applicationContext的前期的初始化工作。如設(shè)置environment毡证,設(shè)置一些內(nèi)部使用的bean电爹,調(diào)用上文提到的一些上下文初始化類ApplicationContextInitializer。觸發(fā)ApplicationContextInitializedEvent事件料睛。設(shè)置beanFactory相關(guān)的參數(shù)及加載資源丐箩,并觸發(fā)ApplicationPreparedEvent事件摇邦。
  9. refreshContext(context),這一步是最重要的一步屎勘,所有的bean初始化涎嚼,注解的處理都在這一步,這一步與spring的基礎(chǔ)功能緊密結(jié)合挑秉。直接調(diào)用spring的入口法梯,refresh()方法。這里只是對整個(gè)啟動(dòng)過程做一下介紹犀概,對這塊接下來的文章中會有詳細(xì)的介紹立哑。
  10. afterRefresh后置處理,默認(rèn)為空方法姻灶。
  11. 計(jì)時(shí)結(jié)束铛绰。觸發(fā)ApplicationStartedEvent事件。
  12. 執(zhí)行ApplicationRunnerCommandLineRunner产喉,這兩個(gè)接口主要是在容器啟動(dòng)完成后捂掰,一些用戶定制化的場景。如加載某些配置曾沈,建立一些連接等这嚣。
    至此,springboot就啟動(dòng)完成了塞俱。接下來我們就可以來進(jìn)入每個(gè)步驟進(jìn)行詳細(xì)的走讀與討論了姐帚。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末弦蹂,一起剝皮案震驚了整個(gè)濱河市霉晕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌位迂,老刑警劉巖唯蝶,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件九秀,死亡現(xiàn)場離奇詭異,居然都是意外死亡粘我,警方通過查閱死者的電腦和手機(jī)鼓蜒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涂滴,“玉大人友酱,你說我怎么就攤上這事∪嶙荩” “怎么了缔杉?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長搁料。 經(jīng)常有香客問我或详,道長系羞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任霸琴,我火速辦了婚禮椒振,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘梧乘。我一直安慰自己澎迎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布选调。 她就那樣靜靜地躺著夹供,像睡著了一般。 火紅的嫁衣襯著肌膚如雪仁堪。 梳的紋絲不亂的頭發(fā)上哮洽,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天,我揣著相機(jī)與錄音弦聂,去河邊找鬼鸟辅。 笑死,一個(gè)胖子當(dāng)著我的面吹牛莺葫,可吹牛的內(nèi)容都是我干的匪凉。 我是一名探鬼主播,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼徙融,長吁一口氣:“原來是場噩夢啊……” “哼洒缀!你這毒婦竟也來了瑰谜?” 一聲冷哼從身側(cè)響起欺冀,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎萨脑,沒想到半個(gè)月后隐轩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡渤早,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年职车,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹊杖。...
    茶點(diǎn)故事閱讀 38,809評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡悴灵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出骂蓖,到底是詐尸還是另有隱情积瞒,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布登下,位于F島的核電站茫孔,受9級特大地震影響叮喳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缰贝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一馍悟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧剩晴,春花似錦锣咒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至嗤攻,卻和暖如春毛嫉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背妇菱。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工承粤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闯团。 一個(gè)月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓辛臊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親房交。 傳聞我的和親對象是個(gè)殘疾皇子彻舰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評論 2 351

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