首頁 > 軟體

SpringBoot詳解執行過程

2022-07-15 14:03:47

每個Spring Boot專案都有一個主程式啟動類,在主程式啟動類中有一個啟動專案的main()方法,在該方法中通過執行SpringApplication.run()即可啟動整個Spring Boot程式。

問題:那麼SpringApplication.run()方法到底是如何做到啟動Spring Boot專案的呢?

下面我們檢視run()方法內部的原始碼,核心程式碼具體如下:

@SpringBootApplication
public class SpringbootDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootDemoApplication.class, args);
    }
}
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return run(new Class[]{primarySource}, args);
}
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
}

從上述原始碼可以看出,SpringApplication.run()方法內部執行了兩個操作,分別是SpringApplication範例的初始化建立和呼叫run()啟動專案,這兩個階段的實現具體說明如下

1.SpringApplication範例的初始化建立

檢視SpringApplication範例物件初始化建立的原始碼資訊,核心程式碼具體如下

    public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
    // 把專案啟動類.class設定為屬性儲存起來
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    // 判斷當前webApplicationType應用的型別
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 設定初始化器(Initializer),最後會呼叫這些初始化器
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 設定監聽器(Listener)
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    // 用於推斷並設定專案main()方法啟動的主程式啟動類
        this.mainApplicationClass = this.deduceMainApplicationClass();

從上述原始碼可以看出,SpringApplication的初始化過程主要包括4部分,具體說明如下。

(1)this.webApplicationType = WebApplicationType.deduceFromClasspath()

用於判斷當前webApplicationType應用的型別。deduceFromClasspath()方法用於檢視Classpath類路徑下是否存在某個特徵類,從而判斷當前webApplicationType型別是SERVLET應用(Spring 5之前的傳統MVC應用)還是REACTIVE應用(Spring 5開始出現的WebFlux互動式應用)

(2)this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class))

用於SpringApplication應用的初始化器設定。在初始化器設定過程中,會使用Spring類載入器SpringFactoriesLoader從META-INF/spring.factories類路徑下的META-INF下的spring.factores檔案中獲取所有可用的應用初始化器類ApplicationContextInitializer。

(3)this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class))

用於SpringApplication應用的監聽器設定。監聽器設定的過程與上一步初始化器設定的過程基本一樣,也是使用SpringFactoriesLoader從META-INF/spring.factories類路徑下的META-INF下的spring.factores檔案中獲取所有可用的監聽器類ApplicationListener。

(4)this.mainApplicationClass = this.deduceMainApplicationClass()

用於推斷並設定專案main()方法啟動的主程式啟動類

2.專案的初始化啟動

分析完(new SpringApplication(primarySources)).run(args)原始碼前一部分SpringApplication範例物件的初始化建立後,檢視run(args)方法執行的專案初始化啟動過程,核心程式碼具體如下:

  public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
    // 第一步:獲取並啟動監聽器
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();
        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    // 第二步:根據SpringApplicationRunListeners以及引數來準備環境
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
    // 準備Banner列印器 - 就是啟動Spring Boot的時候列印在console上的ASCII藝術字型
            Banner printedBanner = this.printBanner(environment);
    // 第三步:建立Spring容器
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, new Object[]{context});
    // 第四步:Spring容器前置處理
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    // 第五步:重新整理容器
            this.refreshContext(context);
    // 第六步:Spring容器後置處理
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
    // 第七步:發出結束執行的事件
            listeners.started(context);
    // 返回容器
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }
        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners) null);
            throw new IllegalStateException(var9);
        }
    }

從上述原始碼可以看出,專案初始化啟動過程大致包括以下部分:

第一步:獲取並啟動監聽器

this.getRunListeners(args)和listeners.starting()方法主要用於獲取SpringApplication範例初始化過程中初始化的SpringApplicationRunListener監聽器並執行。

第二步:根據SpringApplicationRunListeners以及引數來準備環境

this.prepareEnvironment(listeners, applicationArguments)方法主要用於對專案執行環境進行預設定,同時通過this.configureIgnoreBeanInfo(environment)方法排除一些不需要的執行環境

第三步:建立Spring容器

根據webApplicationType進行判斷, 確定容器型別,如果該型別為SERVLET型別,會通過反射裝載對應的位元組碼,也就是AnnotationConfigServletWebServerApplicationContext,接著使用之前初始化設定的context(應用上下文環境)、environment(專案執行環境)、listeners(執行監聽器)、applicationArguments(專案引數)和printedBanner(專案圖示資訊)進行應用上下文的組裝設定,並重新整理設定

第四步:Spring容器前置處理

這一步主要是在容器重新整理之前的準備動作。設定容器環境,包括各種變數等等,其中包含一個非常關鍵的操作:將啟動類注入容器,為後續開啟自動化設定奠定基礎

第五步:重新整理容器

開啟重新整理spring容器,通過refresh方法對整個IOC容器的初始化(包括bean資源的定位,解析,註冊等等),同時向JVM執行時註冊一個關機勾點,在JVM關機時會關閉這個上下文,除非當時它已經關閉

第六步:Spring容器後置處理

擴充套件介面,設計模式中的模板方法,預設為空實現。如果有自定義需求,可以重寫該方法。比如列印一些啟動結束log,或者一些其它後置處理。

第七步:發出結束執行的事件

獲取EventPublishingRunListener監聽器,並執行其started方法,並且將建立的Spring容器傳進去了,建立一個ApplicationStartedEvent事件,並執行ConfigurableApplicationContext 的

publishEvent方法,也就是說這裡是在Spring容器中釋出事件,並不是在SpringApplication中釋出事件,和前面的starting是不同的,前面的starting是直接向SpringApplication中的監聽器釋出啟動事件。

第八步:執行Runners

用於呼叫專案中自定義的執行器XxxRunner類,使得在專案啟動完成後立即執行一些特定程式。其中,Spring Boot提供的執行器介面有ApplicationRunner 和CommandLineRunner兩種,在使用時只需要自定義一個執行器類實現其中一個介面並重寫對應的run()方法介面,然後Spring Boot專案啟動後會立即執行這些特定程式

下面,通過一個Spring Boot執行流程圖,讓大家更清晰的知道Spring Boot的整體執行流程和主要啟動階段:

到此這篇關於SpringBoot詳解執行過程的文章就介紹到這了,更多相關SpringBoot執行過程內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com