SpringBoot原理淺析2-SpringApplication執(zhí)行流程

前言

在上一節(jié)中我們看到SpringBoot方便快捷的POM依賴,這一節(jié)我們主要來看下SpringApplication.run的執(zhí)行流程。

public static void main(String[] args) throws Exception {
    SpringApplication.run(StartSpringBootMain.class, args);
}

雖然只有一行代碼,不過這一行代碼卻做了大量的工作。這行代碼可以分成兩個(gè)部分:1、SpringApplication對象的創(chuàng)建;2、Spring容器的創(chuàng)建(即run方法的執(zhí)行)

說明:本節(jié)源碼的分析基于spring-boot-1.5.4.RELEASE

SpringApplication對象的創(chuàng)建

SpringApplication.run()的調(diào)用執(zhí)行過程中會創(chuàng)建出SpringApplication對象,然后委托給該對象的run方法。

public SpringApplication(Object... sources) {
    /*
     *sources="cn.zgc.springboot.basic.StartSpringBootMain",
     *同時(shí)可以發(fā)現(xiàn)SpringApplication.run方法可以接收多個(gè)啟動配置類。
     */
    initialize(sources);
}

private void initialize(Object[] sources) {
    if (sources != null && sources.length > 0) {
    this.sources.addAll(Arrays.asList(sources));
    }
    // 判斷當(dāng)前應(yīng)用是否為web應(yīng)用
    this.webEnvironment = deduceWebEnvironment();
    // 加載所有可用的ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(
    ApplicationContextInitializer.class));
    // 加載所有可用的ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 判斷main方法的啟動類
    this.mainApplicationClass = deduceMainApplicationClass();
}

SpringApplication類在實(shí)例化的過程中做了以下事情:
1、判斷當(dāng)前應(yīng)用類型是web還是標(biāo)準(zhǔn)的Standalone,這是因?yàn)樗鼈儗?yīng)的ApplicaitonContext類不同。通過deduceWebEnvironment方法完成,在該方法中會檢測classpath中是否同時(shí)存在類"javax.servlet.Servlet"和 "org.springframework.web.context.ConfigurableWebApplicationContext";
2、加載Classpath中所有的ApplicationContextInitializer類;
3、加載classpath中所有的ApplicationListener類;
4、推斷并設(shè)置main方法的定義類。

deduceWebEnvironment()的源碼如下

private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
 "org.springframework.web.context.ConfigurableWebApplicationContext" };

private boolean deduceWebEnvironment() {
    for (String className : WEB_ENVIRONMENT_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return false;
        }
    }
    return true;
}

ApplicationContextInitializer的作用

ApplicationContextInitializer接口是在spring容器刷新之前執(zhí)行的一個(gè)回調(diào)函數(shù),在Spring容器創(chuàng)建之前會先調(diào)用ApplicationContextInitializer類中的initialize方法

默認(rèn)加載的ApplicationContextInitializer

那默認(rèn)加載的這些ApplicationContextInitializer類名是從哪里獲取的呢?通過SpringFactoriesLoader.loadFactoryNames(type, classLoader)讀取spring-boot-1.5.4.RELEASE.jar/META-INF/spring.factories和spring-boot-autoconfigure.1.5.4.RELEASE.jar/META-INF/spring.factories文件中的org.springframework.context.ApplicationContextInitializer定義的值。

ApplicationListener的作用

ApplicationListener用來在Spring容器初始化完成之后,進(jìn)行一些常用的操作,例如初始化緩存、特定任務(wù)的注冊等。在SpringApplication類的初始化的過程中,會默認(rèn)加載10個(gè)ApplicationListener類。這些類和ApplicationContextInitializer一樣定義在spring.factories文件中。

創(chuàng)建容器

實(shí)例化完SpringApplication之后,接著會調(diào)用run方法。run方法執(zhí)行完之后,Spring容器也創(chuàng)建好了,先來看看run的源碼。

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    FailureAnalyzers analyzers = null;
    configureHeadlessProperty();
    // 通過SpringFactoriesLoader查找并加載所有的SpringApplicationRunListener
    // SpringApplicationRunListener 可以監(jiān)聽springboot應(yīng)用啟動過程中的一些生命周期事件,并做一些處理
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 調(diào)用SpringFactoriesLoader的starting方法,廣播SpringBoot要開始執(zhí)行了。
    listeners.starting();
    try {

        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);
        //創(chuàng)建并配置當(dāng)前SpringBoot應(yīng)用將要使用的Environment(包括配置要使用的PropertySource以及Profile),
        //并遍歷調(diào)用所有的SpringApplicationRunListener的environmentPrepared()方法,廣播Environment準(zhǔn)備完畢。
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);
        // 打印Banner,Banner圖案支持自定義
        Banner printedBanner = printBanner(environment);
        //根據(jù)webEnvironment的值來決定創(chuàng)建何種類型的ApplicationContext對象
        //如果是web環(huán)境,則創(chuàng)建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
        //否則創(chuàng)建org.springframework.context.annotation.AnnotationConfigApplicationContext
        context = createApplicationContext();
        // 異常分析器
        analyzers = new FailureAnalyzers(context);
        //為ApplicationContext加載environment,之后逐個(gè)執(zhí)行ApplicationContextInitializer的initialize()方法來進(jìn)一步封裝ApplicationContext,
        //并調(diào)用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一個(gè)空的contextPrepared()方法】,
        //之后初始化IoC容器,并調(diào)用SpringApplicationRunListener的contextLoaded()方法,廣播ApplicationContext的IoC加載完成,
        //這里就包括通過@EnableAutoConfiguration導(dǎo)入的各種自動配置類。
        prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);
        //初始化所有通過@EnableAutoConfiguration導(dǎo)入的各種自動配置類,調(diào)用ApplicationContext的refresh()方法
        refreshContext(context);
        //遍歷所有注冊的ApplicationRunner和CommandLineRunner,并執(zhí)行其run()方法。
        //該過程可以理解為是SpringBoot完成ApplicationContext初始化前的最后一步工作,
        //我們可以實(shí)現(xiàn)自己的ApplicationRunner或者CommandLineRunner,來對SpringBoot的啟動過程進(jìn)行擴(kuò)展。
        afterRefresh(context, applicationArguments);
        // 調(diào)用所有的SpringApplicationRunListener的finished()方法,廣播SpringBoot已經(jīng)完成了ApplicationContext初始化的全部過程。
        listeners.finished(context, null);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
        return context;
    }catch (Throwable ex) {
        handleRunFailure(context, listeners, analyzers, ex);
        throw new IllegalStateException(ex);
    }
}

SpringApplication.run方法的執(zhí)行就是創(chuàng)建Spring容器的過程。run方法執(zhí)行過程中將SpringBoot的生命周期和監(jiān)聽器類SpringApplicationRunListener類綁定在一起,運(yùn)行過程中通過SpringApplicationRunListener廣播對象的事件。

public interface SpringApplicationRunListener {

     //剛執(zhí)行run方法時(shí)
    void started();
     //環(huán)境建立好時(shí)候
    void environmentPrepared(ConfigurableEnvironment environment);
     //上下文建立好的時(shí)候
    void contextPrepared(ConfigurableApplicationContext context);
    //上下文載入配置時(shí)候
    void contextLoaded(ConfigurableApplicationContext context);
    //上下文刷新完成后,run方法執(zhí)行完之前
    void finished(ConfigurableApplicationContext context, Throwable exception);

}

SpringApplicationRunListener在SpringBoot中只有一個(gè)實(shí)現(xiàn)類org.springframework.boot.context.event.EventPublishingRunListener。它用于在SpringBoot啟動的時(shí)發(fā)布不同的事件類型(ApplicationEvent),如果有哪些ApplicationListener對這些事件敢興趣,則可以接收該事件并進(jìn)行處理。

自動配置類的加載注冊

在上一節(jié)中,我們看到SpringBoot會為我們加載很多自動配置類。而自動配置類是在SpringApplication.run的執(zhí)行過程中加載并注冊到Spring容器中的,更具體點(diǎn)是在SpringApplication.refreshContext()方法中執(zhí)行的。具體方法調(diào)用棧如下:

SpringApplication的refreshContext執(zhí)行過程

可以看到refreshContext的執(zhí)行過程中會涉及到Bean后置處理器ConfigurationClassPostProcessor(后置處理器是Spring中很重要的一種機(jī)制,后面單獨(dú)寫篇文章介紹一下)。而ConfigurationClassPostProcessor又會去調(diào)用ConfigurationClassParser對自動配置類進(jìn)行解析。ConfigurationClassParser是個(gè)局部變量,它定義在ConfigurationClassPostProcessor中的processConfigBeanDefinitions方法內(nèi)。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {

    // Parse each @Configuration class
    ConfigurationClassParser parser = new ConfigurationClassParser(
            this.metadataReaderFactory, this.problemReporter, this.environment,
            this.resourceLoader, this.componentScanBeanNameGenerator, registry);
    
    ...
}

由注釋可以知道ConfigurationClassParser類的作用是解析帶有@Configuration注解的類。

擴(kuò)展閱讀

  1. ApplicationContextInitializer接口

  2. Spring 工具類 ConfigurationClassParser 是如何工作的 ?

3.【深入SpringBoot 第三章】SpringApplicationRunListener及其周期

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容