1. 程式人生 > >springboot啟動流程簡析

springboot啟動流程簡析

Spring Boot可以輕鬆建立獨立的,生產級的基於Spring的應用程式,而這隻需要很少的一些Spring配置。本文將從SpringBoot的啟動流程角度簡要的分析SpringBoot啟動過程中主要做了哪些事情。

說明: springboot 2.0.6.RELEASE


SpringBoot啟動簡要流程圖

SpringBoot啟動流程

原始大圖連結

啟動流程概述

啟動流程從角度來看,主要分兩個步驟。第一個步驟是構造一個SpringApplication應用,第二個步驟是呼叫它的run方法,啟動應用。

1 構造SpringApplication應用

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        //資源載入器預設為null
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        //primarySources這裡指的是執行main方法的主類
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        //根據classpath推斷出應用型別,主要有NONE,SERVLET,REACTIVE三種類型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //通過SpringFactoriesLoader工具類獲取META-INF/spring.factories中配置的一系列
        //ApplicationContextInitializer介面的子實現類
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        //基本同上,也是通過SpringFactoriesLoader工具類獲取配置的ApplicationListener
        //介面的子實現類
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        //通過方法呼叫堆疊獲取到main執行方法的主類
        this.mainApplicationClass = deduceMainApplicationClass();
    }

SpringApplication的建構函式中為SpringApplication做了一些初始化配置,包括
主資源類(一般是啟動類 primarySources )、
應用型別(webApplicationType )、
應用環境上下文初始化器(initializers)、
應用監聽器(listeners)、
main方法主類(mainApplicationClass )

initializers將在ConfigurableApplicationContext的refresh方法之前呼叫,比如針對web應用來說,需要在refresh之前註冊屬性源或者啟用指定的配置檔案。

listeners將在AbstractApplicationContext的refresh()方法中,先被註冊到IOC容器中,IOC容器中剩下的非懶載入的單例被例項化後,IOC容器釋出相應的事件,這些事件最終會呼叫與之相關聯的AplicationListener的onApplicationEvent方法

可對照文章頂部的流程圖和原始碼分析


2 執行SpringApplication執行

    /**建立並重新整理應用上下文**/
    public ConfigurableApplicationContext run(String... args) {
        //spring-core包下的計時器類,在這裡主要用來記錄應用啟動的耗時
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        
        //系統配置設定為headless模式
        configureHeadlessProperty();
        //通過SpringFactoriesLoader工具類獲取META-INF/spring.factories
        //檔案中SpringApplicationRunListeners介面的實現類,在這裡是
        //EventPublishingRunListener
        SpringApplicationRunListeners listeners = getRunListeners(args);
        
        //廣播ApplicationStartingEvent事件使應用中的ApplicationListener
        //響應該事件
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            //準備應用環境,這裡會釋出ApplicationEnvironmentPreparedEvent事件
            //並將environment繫結到SpringApplication中
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            
            configureIgnoreBeanInfo(environment);
            //列印彩蛋
            Banner printedBanner = printBanner(environment);
            //根據成員變數webApplicationType建立應用上下文
            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);
            }
            //廣播ApplicationStartedEvent事件使應用中的ApplicationListener
            //響應該事件
            listeners.started(context);
            
            //應用上下文中的ApplicationRunner,CommandLineRunner執行run方法
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
            //廣播ApplicationReadyEvent事件使應用中的ApplicationListener
            //響應該事件     
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        //返回應用上下文
        return context;
    }
  • 在啟動過程中,SpringApplicationListener在不同階段通過呼叫自身的不同方法(如starting()、environmentPrepared())釋出相應事件,通知ApplicationListener進行響應。

  • refreshContext(context)方法是構建IOC容器最複雜的一步,絕大多數bean的定義載入以及例項化都在這一步執行。包括但不限於BeanFactoryPostProcessor、BeanPostProcessor、ApplicationEventMulticaster、@Controller,@Component等註解的元件。

  • SpringFactoriesLoader工具類,在SpringApplication的構造過程中、執行過程中都起到了極其重要的作用。SpringBoot的自動化配置功能一個核心依賴點就在該類上,該類通過讀取類路徑下的META-INF/spring.factories檔案獲取各種各樣的工廠介面的實現類,通過反射獲取這些類的類物件、構造方法,最終生成例項。

總結

  1. SpringApplication的構造過程中,配置了SpringApplication應用上下文的一些基本元素,如應用型別webApplicationType、應用初始化器ApplicationContextInitializer、應用監聽器ApplicationListener等。這些元素在SpringApplication的執行run()過程中,都會在不同階段發揮不同的作用
  2. SpringApplication的執行run()過程中,一方面要初始化IOC容器(主要是bean的載入與初始化),一方面要在不同的階段直接或間接回調ApplicationListener或ApplicationRunner等其他介面的方法
  3. SpringFactoriesLoader是SpringBoot自動化配置功能極其關鍵的一環,它讀取類路徑下META-INF/spring.factories檔案中工廠介面的實現類,來獲取各種各種的Bean工廠例項,最終獲取到自動化配置的bean。

備註:下一篇將通過spring-boot-starter-data-redis來分析SpringBoot對redis的自動化配置是如何操作的,以及SpringFactoriesLoader在SpringApplication啟動過程中的各個階段時發揮了什麼作用?