SpringBoot2.2.6啟動(dòng)run方法之prepareContext

SpringBoot2.2.6啟動(dòng)run方法之prepareContext

前言

此系列是針對(duì)springboot的啟動(dòng)柒爵,旨在于和大家一起來看看springboot啟動(dòng)的過程中到底做了一些什么事馁蒂。文中有不清楚或錯(cuò)誤的地方
歡迎留言指正靡菇。

源碼解讀進(jìn)度

首先我們的源碼閱讀進(jìn)度

public ConfigurableApplicationContext run(String... args) {
    // 用于記錄啟動(dòng)時(shí)間
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    // 聲明Spring上下文
    ConfigurableApplicationContext context = null;
    // 聲明啟動(dòng)錯(cuò)誤回掉
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    // 設(shè)置jdk系統(tǒng)屬性java.awt.headless,默認(rèn)情況為true即開啟
    configureHeadlessProperty();
    // 裝飾者模式創(chuàng)建啟動(dòng)監(jiān)聽器(EventPublishingRunListener實(shí)例)
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 觸發(fā)ApplicationStartingEvent事件灵临,包括轉(zhuǎn)換器的初始化
    listeners.starting();
    try {
        // 參數(shù)封裝潮瓶,也就是在命令行下啟動(dòng)應(yīng)用帶的參數(shù)
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 準(zhǔn)備環(huán)境:1、加載外部化配置的資源到environment晌梨;2桥嗤、觸發(fā)ApplicationEnvironmentPreparedEvent事件
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        // 配置spring.beaninfo.ignore,并添加到名叫systemProperties的PropertySource中仔蝌;默認(rèn)為true即開啟
        configureIgnoreBeanInfo(environment);
        // 打印banner圖
        Banner printedBanner = printBanner(environment);
        // 創(chuàng)建應(yīng)用上下文
        context = createApplicationContext();
        // 創(chuàng)建異常報(bào)告對(duì)象泛领,報(bào)告異常原因
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        // 初始化上下文,本文重點(diǎn)
        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;
}

prepareContext做了哪些事情

首先看代碼掌逛,我們會(huì)在代碼上做相應(yīng)的注釋师逸,后面需要詳細(xì)分析的,前面都會(huì)標(biāo)注序號(hào)

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    // 給上下文對(duì)象設(shè)置環(huán)境對(duì)象豆混,給AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner設(shè)置環(huán)境對(duì)象
    context.setEnvironment(environment);
    // 1. 上下文后置處理篓像,包括向BeanFactory中注冊(cè)BeanNameGenerator和ConversionService
    postProcessApplicationContext(context);
    // 2. SpringApplication構(gòu)造器中初始化了各種ApplicationContextInitializer,循環(huán)調(diào)用他們的initialize方法
    applyInitializers(context);
    // 3. 發(fā)送ApplicationContextInitializedEvent事件
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        // 打印啟動(dòng)信息皿伺,包括pid员辩,用戶等
        logStartupInfo(context.getParent() == null);
        // 答應(yīng)profile信息
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    // 將ApplicationArguments注冊(cè)到容器中
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        // 將Banner注冊(cè)到容器中
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        // 設(shè)置不允許定義同名的BeanDefinition,重復(fù)注冊(cè)時(shí)拋出異常
        ((DefaultListableBeanFactory) beanFactory)
                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    if (this.lazyInitialization) {
        // 如果懶加載鸵鸥,添加懶加載后置處理器
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    // Load the sources
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[0]));
    // 發(fā)送ApplicationPreparedEvent事件
    listeners.contextLoaded(context);
}

下面詳細(xì)分析一下重要的幾個(gè)步驟

    1. postProcessApplicationContext
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
      // BeanNameGenerator是生成bean名字的接口奠滑,用戶可以自己實(shí)現(xiàn)。用戶沒有設(shè)置自己的BeanNameGenerator的時(shí)候
      // AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner的默認(rèn)實(shí)現(xiàn)是AnnotationBeanNameGenerator
      // AnnotationBeanNameGenerator生成名稱規(guī)則是判斷注解中的value是否有值妒穴,沒有的話使用類名首字母小寫作為bean名
      // 如果用戶自定義了beanNameGenerator宋税,prepareContext方法里面的load(context, sources.toArray(new Object[0]));這一步會(huì)設(shè)置
    if (this.beanNameGenerator != null) {
          // 默認(rèn)情況下不會(huì)走到這一步,將用戶自定義的beanNameGenerator注冊(cè)到beanfactory中
        context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
                this.beanNameGenerator);
    }
    // 默認(rèn)不走這一步
    // 如果用戶設(shè)置了自己的resourceLoader讼油,會(huì)設(shè)置resourceLoader到GenericApplicationContext(SpringBoot默認(rèn)使用環(huán)境)
    // 然后看一下GenericApplicationContext的getRoesource方法杰赛,
    // @Override
    // public Resource getResource(String location) {
      //      if (this.resourceLoader != null) {
    //        return this.resourceLoader.getResource(location);
    //    }
    //    return super.getResource(location);
    // }
      // 看到如果設(shè)置了resourceLoader就調(diào)用resourceLoader的getResource,否則調(diào)用
    // GenericApplicationContext的父類DefaultResourceLoader的getResource方法
    if (this.resourceLoader != null) {
        if (context instanceof GenericApplicationContext) {
            ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
        }
        if (context instanceof DefaultResourceLoader) {
            ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
        }
    }
    // 默認(rèn)會(huì)走該方法
    // 上文中我們分析createApplicationContext的時(shí)候矮台,說過構(gòu)造器中初始化了DefaultListableBeanFactory實(shí)例對(duì)象
    // 所以這里context.getBeanFactory()拿到的就是DefaultListableBeanFactory實(shí)例
    // setConversionService設(shè)置個(gè)各種轉(zhuǎn)換器乏屯,比如:調(diào)用接口時(shí)傳了一個(gè)參數(shù)為age=“18”,
    // 接口接收參數(shù)為Integer類型瘦赫,轉(zhuǎn)換器StringToNumberConverterFactory就會(huì)將String轉(zhuǎn)換為Integer
    if (this.addConversionService) {
        context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
    }
}
    1. applyInitializers
protected void applyInitializers(ConfigurableApplicationContext context) {
        // SpringApplication構(gòu)造器中初始化了各種ApplicationContextInitializer辰晕,getInitializers()方法把他們排序并放到Set中
        // 1. DelegatingApplicationContextInitializer 從environment中獲取context.initializer.classes屬性,默認(rèn)為null确虱。
        // 如果配置了數(shù)據(jù)并且是一個(gè)逗號(hào)分隔的列表含友,分別判斷是不是ApplicationContextInitializer的實(shí)現(xiàn),然后調(diào)用initialize方法.
        // 2. SharedMetadataReaderFactoryContextInitializer  向beanFactoryPostProcessors(beanFactory后置處理器)中
        // 添加一個(gè)CachingMetadataReaderFactoryPostProcessor。該后置處理器會(huì)在后面講到的refreshContext方法中會(huì)執(zhí)行唱较,
        // 在執(zhí)行方法中向beanFactory中注冊(cè)一個(gè) BeanDefinition(SharedMetadataReaderFactoryBean)
        // 然后向internalConfigurationAnnotationProcessor的BeanDefinition中注冊(cè)一個(gè)RuntimeBeanReference扎唾,在Bean實(shí)例化的
        // 時(shí)候設(shè)置metadataReaderFactory為SharedMetadataReaderFactoryBean
        // 3. ContextIdApplicationContextInitializer 設(shè)置ContextId,通過spring.application.name設(shè)置
        // 4. ConfigurationWarningsApplicationContextInitializer 向beanFactoryPostProcessors(beanFactory后置處理器)中
        // 添加一個(gè)ConfigurationWarningsPostProcessor南缓,作用是添加一下檢查。默認(rèn)有一個(gè)ComponentScanPackageCheck荧呐,作用是檢查@ComponentScan
        // 掃描的包路徑是否合法
        // 5. ServerPortInfoApplicationContextInitializer 添加一個(gè)ApplicationListener汉形。監(jiān)聽WebServerInitializedEvent事件,
        // 向Environment中添加端口號(hào)local.sever.port
        // 6. ConditionEvaluationReportLoggingListener 監(jiān)聽ContextRefreshedEvent和ApplicationFailedEvent事件倍阐,并做日志
    for (ApplicationContextInitializer initializer : getInitializers()) {
        Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
                ApplicationContextInitializer.class);
        Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
        initializer.initialize(context);
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末概疆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子峰搪,更是在濱河造成了極大的恐慌岔冀,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件概耻,死亡現(xiàn)場(chǎng)離奇詭異使套,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)鞠柄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門侦高,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人厌杜,你說我怎么就攤上這事奉呛。” “怎么了夯尽?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵瞧壮,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我匙握,道長(zhǎng)咆槽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任肺孤,我火速辦了婚禮罗晕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赠堵。我一直安慰自己小渊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布茫叭。 她就那樣靜靜地躺著酬屉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上呐萨,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天杀饵,我揣著相機(jī)與錄音,去河邊找鬼谬擦。 笑死切距,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的惨远。 我是一名探鬼主播谜悟,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼北秽!你這毒婦竟也來了葡幸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤贺氓,失蹤者是張志新(化名)和其女友劉穎蔚叨,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辙培,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蔑水,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了虏冻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肤粱。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖厨相,靈堂內(nèi)的尸體忽然破棺而出领曼,到底是詐尸還是另有隱情,我是刑警寧澤蛮穿,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布庶骄,位于F島的核電站,受9級(jí)特大地震影響践磅,放射性物質(zhì)發(fā)生泄漏单刁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一府适、第九天 我趴在偏房一處隱蔽的房頂上張望羔飞。 院中可真熱鬧,春花似錦檐春、人聲如沸逻淌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卡儒。三九已至田柔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間骨望,已是汗流浹背硬爆。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留擎鸠,地道東北人缀磕。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像劣光,于是被迫代替她去往敵國(guó)和親虐骑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355