說明:
本系列基於SpringBoot 2.2.9.RELEASE 版本,對SpringBoot的原理進行分析,一共分爲四節:
SpringBoot原理髮現(一):創建Hello World,對pom依賴以及@SpringBootApplication註解進行分析
SpringBoot原理髮現(二):分析SpringBoot自動配置原理
SpringBoot原理髮現(三):通過主配置類main方法分析SpringBoot啓動配置原理
SpringBoot原理髮現(四):瞭解SpringBoot啓動中的幾個重要回調機制
SpringBoot 啓動配置原理
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }
啓動運行流程分析
debug上面主配置類的run方法,它會執行到如下代碼:
由此可見,主配置類run方法分爲兩個部分。
第一步 :new SpringApplication(primarySources),創建出SrpingApplication對象
第二步 :調用返回對象SpringApplication.run(args)
1. new SpringApplication(primarySources)
跟蹤new SpringApplication(primarySources),最終會執行SpringApplication的構造方法,如下:
根據方法形參可以看出配置類其實可以傳入多個,構造函數執行流程解釋如下:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; //檢查主要來源類是否爲空,此時就是我們傳入的主配置類:class com.example.demo.DemoApplication Assert.notNull(primarySources, "PrimarySources must not be null"); //將主要來源類保存到primarySources中,primarySources:Set<Class<?>> primarySources this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //判斷當前應用的類型,返回WebApplicationType.SERVLET,代表是一個web應用 this.webApplicationType = WebApplicationType.deduceFromClasspath(); /** * 查找類路徑下META-INF/spring.factories 文件中定義的ApplicationContextInitializer的值列表並保存在initializers屬性中 * * 其實和 Springboot原理探究(一) * 2.2.2 @Import(AutoConfigurationImportSelector.class) 同理 * 只要是getSpringFactoriesInstances()就是去查找類路徑下META-INF/spring.factories文件指定的值列表 */ setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //同上面一樣,查找類路徑下META-INF/spring.factories 文件中定義的ApplicationListener的值列表並保存在listeners屬性中 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //查找配置類中有main方法的主配置類 this.mainApplicationClass = deduceMainApplicationClass(); }
META-INF/spring.factories
# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer
至此new SpringApplication(primarySources)執行完畢,並創建出SpringApplication對象 ,可以看出此步驟主要是將 ApplicationContextInitializer 和 ApplicationListener 保存起來
2. springApplication.run(args)
調用的run方法解釋如下:
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { //StopWatch開始監聽,設置IOC容器屬性null StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); //AWT應用,忽略 configureHeadlessProperty(); //獲取類路徑下META-INF/spring.factories 文件中SpringApplicationRunListener定義的值,參見下面 2.1 SpringApplicationRunListeners listeners = getRunListeners(args); //回調所有SpringApplicationRunListener的starting(),可以查看SpringApplicationRunListener監聽器中定義的幾個回調方法 ,參見下面 2.2 listeners.starting(); try { //將args封裝成ApplicationArguments對象 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //準備環境,並回調SpringApplicationRunListener的environmentPrepared方法,參見下面 2.3 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); //打印Spring Banner圖 Banner printedBanner = printBanner(environment); //通過BeanUtils反射創建IOC容器,此處會根據this.webApplicationType判斷是否爲web環境,而this.webApplicationType已經在第一大步驟中得到 context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); /** 準備上下文:將environment保存在IOC容器中,
* 並 回調所有的ApplicationContextInitializer的initialize方法, ApplicationContextInitializer則是在第一大步驟中已經獲取且保存了 * 回調所有SpringApplicationRunListener的contextPrepared方法, * 回調所有SpringApplicationRunListener的contextLoaded方法
* 參見下面 2.4
*/ prepareContext(context, environment, listeners, applicationArguments, printedBanner); //刷新容器:初始化IOC,初始化所有的組件,如果是web應用此處還會創建嵌入式的tomcat refreshContext(context); //2.2.9.RELEASE版本的spingboot這就是一個空方法,之前的版本中會調用callRunners方法 afterRefresh(context, applicationArguments); //StopWatch停止監聽 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //調用所有SpringApplicationRunListener的started方法 listeners.started(context); //從IOC容器中獲取ApplicationRunner 和 CommandLineRunner, SpringBoot中比較重要的幾個回調機制
//先回調ApplicationRunner的run在回調CommandLineRunner callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //回調所有SpringApplicationRunListener的running方法 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } //返回IOC容器 return context; }
2.1 SpringApplicationRunListener 監聽器
SpringBoot中比較重要的回調機制之一
可以看出又是getSpringFactoriesInstances方法,只要是getSpringFactoriesInstances()就是去查找類路徑下META-INF/spring.factories文件指定的值列表,而這裏就是去獲取SpringApplicationRunListener的值列表
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); }
2.2 SpringApplicationRunListener監聽器
回調SpringApplicationRunListener 的 starting方法
void starting() { for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); } }
2.3 prepareEnvironment(listeners, applicationArguments) 環境準備
回調SpringApplicationRunListener 的 environmentPrepared方法
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 創建環境 ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); //回調SpringApplicationRunListener的environmentPrepared方法,環境準備完成 listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
2.4 準備上下文
回調之前已經保存的所有ApplicationContextInitializer的initialize方法
回調所有SpringApplicationRunListener的contextPrepared方法
回調所有SpringApplicationRunListener的contextLoaded方法
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); //回調所有的ApplicationContextInitializer的initialize方法,而ApplicationContextInitializer就是之前創建SpringApplication時保存的 applyInitializers(context); //回調所有SpringApplicationRunListener的contextPrepared方法 listeners.contextPrepared(context); //日誌記錄 if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); //回調所有SpringApplicationRunListener的contextLoaded方法 listeners.contextLoaded(context); }
至此springApplication.run(args)執行完畢,可以看出第二大步驟就是環境準備,初始化IOC容器,掃描Bean創建組件,並調用幾個重要的回調機制。
SpringBoot中四個重要的回調機制
1. ApplicationContextInitializer
在第一大步驟中掃描META-INF/spring.factories 文件得到,在第二大步驟中回調其initialize方法
2. SpringApplicationRunListener
在第二大步驟中掃描META-INF/spring.factories 文件得到,並依次回調
a. starting()
b. environmentPrepared()
c. contextPrepared()
d. contextLoaded()
e. started()
f. running()
3. ApplicationRunner
在第二大步驟中,通過IOC容器獲取,且回調run方法
4. CommandLineRunner
在第二大步驟中,通過IOC容器獲取,且回調run方法