springboot啟動流程簡析
Spring Boot可以輕鬆建立獨立的,生產級的基於Spring的應用程式,而這隻需要很少的一些Spring配置。本文將從SpringBoot的啟動流程角度簡要的分析SpringBoot啟動過程中主要做了哪些事情。
說明: springboot 2.0.6.RELEASE
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
檔案獲取各種各樣的工廠介面的實現類,通過反射獲取這些類的類物件、構造方法,最終生成例項。
總結
- SpringApplication的構造過程中,配置了SpringApplication應用上下文的一些基本元素,如應用型別webApplicationType、應用初始化器ApplicationContextInitializer、應用監聽器ApplicationListener等。這些元素在SpringApplication的執行run()過程中,都會在不同階段發揮不同的作用
- SpringApplication的執行run()過程中,一方面要初始化IOC容器(主要是bean的載入與初始化),一方面要在不同的階段直接或間接回調ApplicationListener或ApplicationRunner等其他介面的方法
- SpringFactoriesLoader是SpringBoot自動化配置功能極其關鍵的一環,它讀取類路徑下META-INF/spring.factories檔案中工廠介面的實現類,來獲取各種各種的Bean工廠例項,最終獲取到自動化配置的bean。
備註:下一篇將通過spring-boot-starter-data-redis
來分析SpringBoot對redis的自動化配置是如何操作的,以及SpringFactoriesLoader在SpringApplication啟動過程中的各個階段時發揮了什麼作用?