import org.springframework.boot.SpringApplication;
|
<1> 处,使用 @SpringBootApplication 注解,标明是 Spring Boot 应用。通过它,可以开启自动配置的功能。<2> 处,调用 SpringApplication#run(Class<?>... primarySources) 方法,启动 Spring Boot 应用。上述的代码,是我们使用 Spring Boot 时,最最最常用的代码。而本文,我们先来分析 Spring Boot 应用的启动过程。
关于
@SpringApplication注解,我们会后面单独开文章,详细解析。
org.springframework.boot.SpringApplication ,Spring 应用启动器。正如其代码上所添加的注释,它来提供启动 Spring 应用的功能。
Class that can be used to bootstrap and launch a Spring application from a Java main method.
|
大多数情况下,我们都是使用它提供的静态方法:
// SpringApplication.java
|
SpringApplication#run(Class<?> primarySource, String... args) 方法,运行 Spring 应用。详细解析,见 「2.2 run」 。
// SpringApplication.java
|
resourceLoader 属性,资源加载器。可以暂时不理解,感兴趣的胖友,可以看看 《【死磕 Spring】—— IoC 之 Spring 统一资源加载策略》 文章。primarySources 属性,主要的 Java Config 类的数组。在文初提供的示例,就是 MVCApplication 类。webApplicationType 属性,调用 WebApplicationType#deduceFromClasspath() 方法,通过 classpath ,判断 Web 应用类型。
#createApplicationContext() 方法,将根据它的值(类型),创建不同类型的 ApplicationContext 对象,即 Spring 容器的类型不同。initializers 属性,ApplicationContextInitializer 数组。
#getSpringFactoriesInstances(Class<T> type) 方法,进行获得 ApplicationContextInitializer 类型的对象数组,详细的解析,见 「2.1.1 getSpringFactoriesInstances」 方法。initializers 属性的结果如下图:
listeners 属性,ApplicationListener 数组。
#getSpringFactoriesInstances(Class<T> type) 方法,进行获得 ApplicationListener 类型的对象数组。listeners 属性的结果如下图:
mainApplicationClass 属性,调用 #deduceMainApplicationClass() 方法,获得是调用了哪个 #main(String[] args) 方法,代码如下:
// SpringApplication.java
|
mainApplicationClass 属性,没有什么逻辑上的用途,主要就是用来打印下日志,说明是通过这个类启动 Spring 应用的。#getSpringFactoriesInstances(Class<T> type) 方法,获得指定类类对应的对象们。代码如下:
// SpringApplication.java
|
<1> 处,调用 SpringFactoriesLoader#loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) 方法,加载指定类型对应的,在 META-INF/spring.factories 里的类名的数组。
META-INF/spring.factories 文件中,会以 KEY-VALUE 的格式,配置每个类对应的实现类们。<2> 处,调用 #createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) 方法,创建对象们。代码如下:
// SpringApplication.java
|
<3> 处,调用 AnnotationAwareOrderComparator#sort(List<?> list) 方法,排序对象们。例如说,类上有 @Order 注解。#run(String... args) 方法,运行 Spring 应用。代码如下:
艿艿:这是一个饱满的方法,所以逻辑比较多哈。
// SpringApplication.java
|
<1> 处,创建 StopWatch 对象,并调用 StopWatch#run() 方法来启动。StopWatch 主要用于简单统计 run 启动过程的时长。<2> 处,配置 headless 属性。这个逻辑,可以无视,和 AWT 相关。<3> 处,调用 #getRunListeners(String[] args) 方法,获得 SpringApplicationRunListener 数组,并启动监听。代码如下:
// SpringApplication.java
|
listeners 变量,如下图所示:
listeners 的 ApplicationListener 类型。详细的,我们在 「3. SpringApplicationRunListeners」 中,在详细解析。<4> 处,调用 #prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) 方法,加载属性配置。执行完成后,所有的 environment 的属性都会加载进来,包括 application.properties 和外部的属性配置。详细的,胖友先一起跳到 「2.2.1 prepareEnvironment」 中。
<5> 处,调用 #printBanner(ConfigurableEnvironment environment) 方法,打印 Spring Banner 。效果如下:
. ____ _ __ _ _
|
<6> 处,调用 #createApplicationContext() 方法,创建 Spring 容器。详细解析,见 「2.2.2 createApplicationContext」 。
<7> 处,通过 #getSpringFactoriesInstances(Class<T> type) 方法,进行获得 SpringBootExceptionReporter 类型的对象数组。SpringBootExceptionReporter ,记录启动过程中的异常信息。
exceptionReporters 属性的结果如下图:
<8> 处,调用 #prepareContext(...) 方法,主要是调用所有初始化类的 #initialize(...) 方法。详细解析,见 「2.2.3 prepareContext」 。<9> 处,调用 #refreshContext(ConfigurableApplicationContext context)` 方法,启动(刷新) Spring 容器。详细解析,见 「2.2.4 refreshContext」 。<10> 处,调用 #afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) 方法,执行 Spring 容器的初始化的后置逻辑。默认实现为空。代码如下:
// SpringApplication.java
|
<11> 处,停止 StopWatch 统计时长。
<12> 处,打印 Spring Boot 启动的时长日志。效果如下:
2019-01-28 20:42:03.338 INFO 53001 --- [ main] c.iocoder.springboot.mvc.MVCApplication : Started MVCApplication in 20.893 seconds (JVM running for 23.536)
|
<13> 处,调用 SpringApplicationRunListeners#started(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的数组,Spring 容器启动完成。
<14> 处,调用 #callRunners(ApplicationContext context, ApplicationArguments args) 方法,调用 ApplicationRunner 或者 CommandLineRunner 的运行方法。详细解析,见 「2.2.5 callRunners」 。
<14.1> 处,如果发生异常,则调用 #handleRunFailure(...) 方法,交给 SpringBootExceptionReporter 进行处理,并抛出 IllegalStateException 异常。<15> 处,调用 SpringApplicationRunListeners#running(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的数组,Spring 容器运行中。
<15.1> 处,如果发生异常,则调用 #handleRunFailure(...) 方法,交给 SpringBootExceptionReporter 进行处理,并抛出 IllegalStateException 异常。艿艿:这个方法,大体看下即可。
#prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) 方法,加载属性配置。代码如下:
// SpringApplication.java
|
<1> 处,调用 #getOrCreateEnvironment() 方法,创建 ConfigurableEnvironment 对象。代码如下:
// SpringApplication.java
|
webApplicationType 类型,会创建不同类型的 ConfigurableEnvironment 对象。<servletContextInitParams /> 和 <servletConfigInitParams /> 等配置参数。<1> 处,调用 #configureEnvironment(ConfigurableEnvironment environment, String[] args) 方法,配置 environment 变量。代码如下:
// SpringApplication.java
|
<1.1> 处,设置 environment 的 conversionService 属性。可以暂时无视。感兴趣的胖友,可以看看 《【死磕 Spring】—— 环境 & 属性:PropertySource、Environment、Profile》<1.2> 处,增加 environment 的 PropertySource 属性源。代码如下:
// SpringApplication.java
|
defaultProperties、或者 JVM 启动参数,作为附加的 PropertySource 属性源。<1.3> 处,配置 environment 的 activeProfiles 属性。代码如下:
// SpringApplication.java
|
<2> 处,调用 SpringApplicationRunListeners#environmentPrepared(ConfigurableEnvironment environment) 方法,通知 SpringApplicationRunListener 的数组,环境变量已经准备完成。
<3> 处,调用 #bindToSpringApplication(ConfigurableEnvironment environment) 方法,绑定 environment 到 SpringApplication 上。暂时不太知道用途。<4> 处,如果非自定义 environment ,则根据条件转换。默认情况下,isCustomEnvironment 为 false ,所以会执行这块逻辑。但是,一般情况下,返回的还是 environment 自身,所以可以无视这块逻辑先。<5> 处,调用 ConfigurationPropertySources#attach(Environment environment) 静态方法,如果有 attach 到 environment上的 MutablePropertySources ,则添加到 environment 的 PropertySource 中。这块逻辑,也可以先无视。#createApplicationContext() 方法,创建 Spring 容器。代码如下:
// SpringApplication.java
|
webApplicationType 类型,获得对应的 ApplicationContext 对象。#prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) 方法,准备 ApplicationContext 对象,主要是初始化它的一些属性。代码如下:
// SpringApplication.java
|
context 的属性做赋值,以及 ApplicationContextInitializer 的初始化。<1> 处,设置 context 的 environment 属性。<2> 处,调用 #postProcessApplicationContext(ConfigurableApplicationContext context) 方法,设置 context 的一些属性。代码如下:
// SpringApplication.java
|
<3> 处,调用 #applyInitializers(ConfigurableApplicationContext context) 方法,初始化 ApplicationContextInitializer 。代码如下:
// SpringApplication.java
|
ApplicationContextInitializer#initialize(context) 方法,进行初始化。<4> 处,调用 SpringApplicationRunListeners#contextPrepared(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的数组,Spring 容器准备完成。<5> 处,打印日志。效果如下:
2019-01-28 17:53:31.600 INFO 21846 --- [ main] c.iocoder.springboot.mvc.MVCApplication : Starting MVCApplication on MacBook-Pro-5.local with PID 21846 (/Users/yunai/Java/spring-boot/spring-boot-tests/spring-boot-yunai-tests/spring-boot-yunai-mvc-tests/target/classes started by yunai in /Users/yunai/Java/spring-boot)
|
<6> 处,设置 beanFactory 的属性。
<7> 处,调用 #load(ApplicationContext context, Object[] sources) 方法,加载 BeanDefinition 们。代码如下:
// SpringApplication.java
|
<1> 处,调用 #getBeanDefinitionRegistry(ApplicationContext context) 方法,创建 BeanDefinitionRegistry 对象。代码如下:
// SpringApplication.java
|
<1> 处,调用 #createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) 方法,创建 org.springframework.boot.BeanDefinitionLoader 对象。关于它,后续的文章,详细解析。
<2> 处,设置 loader 的属性。<3> 处,调用 BeanDefinitionLoader#load() 方法,执行 BeanDefinition 加载。关于这一块,胖友感兴趣,先简单看看 《【死磕 Spring】—— IoC 之加载 BeanDefinition》 文章。<8> 处,调用 SpringApplicationRunListeners#contextLoaded(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的数组,Spring 容器加载完成。
#refreshContext(ConfigurableApplicationContext context) 方法,启动(刷新) Spring 容器。代码如下:
// SpringApplication.java
|
<1> 处,调用 #refresh(ApplicationContext applicationContext) 方法,开启(刷新)Spring 容器。代码如下:
// SpringApplication.java
|
AbstractApplicationContext#refresh() 方法,启动(刷新)Spring 容器。
AbstractApplicationContext#refresh() 方法,胖友可以看看 《【死磕 Spring】—— ApplicationContext 相关接口架构分析》 文章。<2> 处,调用 ConfigurableApplicationContext#registerShutdownHook() 方法,注册 ShutdownHook 钩子。这个钩子,主要用于 Spring 应用的关闭时,销毁相应的 Bean 们。#callRunners(ApplicationContext context, ApplicationArguments args) 方法,调用 ApplicationRunner 或者 CommandLineRunner 的运行方法。代码如下:
// SpringApplication.java
|
<1> 处,获得所有 Runner 们,并进行排序。<2> 处,遍历 Runner 数组,执行逻辑。代码如下:
// SpringApplication.java
|
关于 Runner 功能的使用,可以看看 《ApplicationRunner 接口》 和 《CommandLineRunner 接口》 文档。
org.springframework.boot.SpringApplicationRunListeners ,SpringApplicationRunListener 数组的封装。代码如下:
// SpringApplicationRunListeners.java
|
org.springframework.boot.SpringApplicationRunListener ,SpringApplication 运行的监听器接口。代码如下:
// SpringApplicationRunListener.java
|
目前,SpringApplicationRunListener 的实现类,只有 EventPublishingRunListener 类。
org.springframework.boot.context.event.EventPublishingRunListener ,实现 SpringApplicationRunListener、Ordered 接口,将 SpringApplicationRunListener 监听到的事件,转换成对应的 SpringApplicationEvent 事件,发布到监听器们。
代码如下:
// EventPublishingRunListener.java |