SpringBoot源碼解析(一)--啟動(dòng)過(guò)程refresh()方法詳解

spring版本: 5.0.6
springboot版本: 2.0.2
先祭出一張 spring 容器的核心接口圖:

image

spring 容器有兩個(gè)核心接口:BeanFactory 和 ApplicationContext,其中 ApplicationContext是 BeanFactory的子類, 這兩個(gè)類生成并管理 spring 容器中的 bean坦弟。但是大多數(shù)情況都是用ApplicationContext作為spring 的容器累魔。 spring mvc和 spring boot 都是通過(guò) applicationContext作為管理 bean 的容器的鞍陨。 其中 spring boot 默認(rèn)使用的是AnnotationConfigServletWebServerApplicationContext(還可以使用AnnotationConfigReactiveWebServerApplicationContext洪燥,有興趣的同學(xué)可以研究一下霉祸,什么情況下會(huì)使用這個(gè)轻姿。)犁珠, spring boot 類似 spring mvc都是 web server, 因此使用" servlet application context"作為默認(rèn)容器十分可以理解傅瞻。AnnotationConfigServletWebServerApplicationContext本身是一個(gè) BeanFactory的派生類,而它的父 context 也會(huì)持有一個(gè) BeanFactory盲憎。

再來(lái)說(shuō)說(shuō)啟動(dòng)過(guò)程:

springboot 的啟動(dòng)入口是 SpringApplication 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);
          //此處創(chuàng)建 context
            context = createApplicationContext();
            exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
          //初始化context 的核心邏輯都在這行代碼里。
            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;
    }

再具體分析下 refreshContext的代碼邏輯

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
//清除緩存饼疙,并讀取配置文件里的環(huán)境變量
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
                       // 此處的 bean factory 是通過(guò)父類GenericApplicationContext 初始化的 DefaultListableBeanFactory
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
// 配置 beanFactory溺森, 包括注冊(cè)(第一次) BeanPostProcessor, 設(shè)置 classLoader等等。
// BeanPostProcessor 有兩個(gè)主要的方法:postProcessBeforeInitialization 和 postProcessAfterInitialization 窑眯。 
// 這個(gè)兩個(gè)方法分別的執(zhí)行時(shí)期是 bean 初始化之前和初始化之后屏积。具體的執(zhí)行時(shí)期后面會(huì)說(shuō)到

            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
//為 applicationContext的子類注冊(cè)(第二次)個(gè)性化的 BeanPostProcessor
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
//執(zhí)行 context 中注冊(cè)的 BeanFactoryPostProcessor中的postProcessBeanFactory() 方法,BeanFactoryPostProcessor只有這一個(gè)方法
//BeanFactoryPostProcessor 是 bean 屬性處理容器磅甩。即管理 bean工廠中的BeanDefinition, 
// BeanDefinition在 spring mvc 中就是 xml 文件中對(duì)應(yīng)的 bean 標(biāo)簽(注意炊林,是標(biāo)簽,而不是真實(shí)的 JAVA BEAN)卷要。
//可以看到渣聚,此處已經(jīng) invoke 了postProcessBeanFactory() 方法。 而 BeanPostProcessor 的方法還沒(méi)有被調(diào)用僧叉,
//所以 BeanFactoryPostProcessor的執(zhí)行是要早于BeanPostProcessor的奕枝。
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
//再次注冊(cè)BeanPostProcessor(這已經(jīng)是第三次注冊(cè),
// 第一注冊(cè)的是AbstractApplicationContext中用到的 processor, 
// 第二次為ServletWebServerApplicationContext 注冊(cè) processor)瓶堕。
// 這次是為實(shí)際用到的 context注冊(cè) processor(spring boot 即是AnnotationConfigServletWebServerApplicationContext)隘道,
// 并為根據(jù)優(yōu)先級(jí)和 order 排序。
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
// 實(shí)例化并注冊(cè) MessageSource類郎笆。MessageSource是國(guó)際化相關(guān)的接口
                initMessageSource();

                // Initialize event multicaster for this context.
//初始化spring 事件廣播器谭梗,ApplicationContext會(huì)通過(guò)廣播器發(fā)送事件,負(fù)責(zé)監(jiān)聽(tīng)廣播器的 listener 會(huì)負(fù)責(zé)處理事件
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
// 在一些特殊的 ApplicationContext子類中實(shí)例化一些特殊的 bean. 
//例如ServletWebServerApplicationContext(AnnotationConfigServletWebServerApplicationContext的父類)會(huì)初始化TomcatWebServer宛蚓,
//并啟動(dòng)這個(gè) server激捏,在 spring boot 工程中,相當(dāng)于啟動(dòng)了整個(gè) spring boot 項(xiàng)目苍息。
                onRefresh();

                // Check for listener beans and register them.
//為ApplicationEventMulticaster注冊(cè)監(jiān)聽(tīng) listener(ApplicationListener), 
//會(huì)有一些默認(rèn)的 listener被注冊(cè)缩幸。例如這是一個(gè) dubbo provider的工程壹置,就會(huì)注冊(cè)DubboBannerApplicationListener 
// 同時(shí)也會(huì)有一些自定義的 listener.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
//終于到了初始 java bean 的階段竞思, 注意此處只初始化單例的 bean, 并且非懶加載的類(默認(rèn)即非懶加載)。
//雖然此時(shí) java bean已經(jīng)實(shí)例化钞护,但是其依賴自動(dòng)注入的 bean卻不再此時(shí)注入盖喷,而是等到用到的時(shí)候再去獲取,
//所以這里不會(huì)出現(xiàn)循環(huán)依賴的問(wèn)題难咕。
// 一個(gè) spring bean 的初始化過(guò)程如下:
//        1. 執(zhí)行構(gòu)造器
//        2. BeanPostProcessor的postProcessBeforeInitialization方法
//        3. InitializingBean的afterPropertiesSet方法
//        4课梳,@Bean注解的initMethod方法
//        5距辆,BeanPostProcessor的postProcessAfterInitialization方法
//        6,DisposableBean的destroy方法
//        7暮刃,@Bean注解的destroyMethod方法
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
//實(shí)例化LifecycleProcessor并執(zhí)行其 onRefresh()方法跨算,刷新上下文,啟動(dòng)一些需要在初始化階段啟動(dòng)的類(實(shí)現(xiàn)了Lifecycle接口的類)
// 發(fā)布ContextRefreshedEvent事件椭懊,這個(gè)應(yīng)該是表示上下文啟動(dòng)完成的事件
                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...
//清理單例 bean 的元數(shù)據(jù)緩存诸蚕。
                resetCommonCaches();
            }
        }
    }

至此, spring boot的啟動(dòng)過(guò)程就完成了氧猬, 基于篇幅和時(shí)間問(wèn)題背犯。其中還有很多代碼細(xì)節(jié)沒(méi)有仔細(xì)分析,這些就留給未來(lái)吧盅抚。

整個(gè)過(guò)程分析下來(lái)發(fā)現(xiàn)這個(gè)啟動(dòng)過(guò)程會(huì)有幾個(gè)核心接口(應(yīng)該也是 spring 整個(gè) ioc機(jī)制的核心接口)

  1. BeanFactory
  2. ApplicationContext
  3. BeanPostProcessor
  4. BeanFactoryPostProcessor
  5. ApplicationEventMulticaster
  6. BeanDefinition
  7. LifecycleProcessor

以及每個(gè)接口里面的方法及其執(zhí)行時(shí)序漠魏。
弄清楚每一個(gè)接口的原理和具體使用場(chǎng)景,對(duì)我們使用和理解 spring 至關(guān)重要妄均。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末柱锹,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子丰包,更是在濱河造成了極大的恐慌奕纫,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烫沙,死亡現(xiàn)場(chǎng)離奇詭異匹层,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)锌蓄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門升筏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人瘸爽,你說(shuō)我怎么就攤上這事您访。” “怎么了剪决?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵灵汪,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我柑潦,道長(zhǎng)享言,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任渗鬼,我火速辦了婚禮览露,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘譬胎。我一直安慰自己差牛,他們只是感情好命锄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著偏化,像睡著了一般脐恩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上侦讨,一...
    開(kāi)封第一講書(shū)人閱讀 51,590評(píng)論 1 305
  • 那天被盈,我揣著相機(jī)與錄音,去河邊找鬼搭伤。 笑死只怎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的怜俐。 我是一名探鬼主播身堡,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拍鲤!你這毒婦竟也來(lái)了贴谎?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤季稳,失蹤者是張志新(化名)和其女友劉穎擅这,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體景鼠,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仲翎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了铛漓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片溯香。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖浓恶,靈堂內(nèi)的尸體忽然破棺而出玫坛,到底是詐尸還是另有隱情,我是刑警寧澤包晰,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布湿镀,位于F島的核電站,受9級(jí)特大地震影響伐憾,放射性物質(zhì)發(fā)生泄漏勉痴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一塞耕、第九天 我趴在偏房一處隱蔽的房頂上張望蚀腿。 院中可真熱鬧,春花似錦扫外、人聲如沸莉钙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)磁玉。三九已至,卻和暖如春驾讲,著一層夾襖步出監(jiān)牢的瞬間蚊伞,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工吮铭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留时迫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓谓晌,卻偏偏與公主長(zhǎng)得像掠拳,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子纸肉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355