code-learning/springboot/07-Spring Boot 源码分析-SpringApplication.md

1243 lines
49 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 精尽 Spring Boot 源码分析 —— SpringApplication
# 1. 概述
```
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // <1>
public class MVCApplication {
public static void main(String[] args) {
SpringApplication.run(MVCApplication.class, args); // <2>
}
}
```
- `<1>` 处,使用 `@SpringBootApplication` 注解,标明是 Spring Boot 应用。通过它,可以开启自动配置的功能。
- `<2>` 处,调用 `SpringApplication#run(Class<?>... primarySources)` 方法,启动 Spring Boot 应用。
上述的代码,是我们使用 Spring Boot 时,最最最常用的代码。而本文,我们先来分析 Spring Boot 应用的**启动过程**。
> 关于 `@SpringApplication` 注解,我们会后面单独开文章,详细解析。
# 2. 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
public static void main(String[] args) throws Exception {
SpringApplication.run(new Class<?>[0], args);
}
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 创建 SpringApplication 对象,并执行运行。
return new SpringApplication(primarySources).run(args);
}
```
- 前两个静态方法,最终调用的是第 3 个静态方法。而第 3 个静态方法,实现的逻辑就是:
- 首先,创建一个 SpringApplication 对象。详细的解析,见 [「2.1 构造方法」](https://svip.iocoder.cn/Spring-Boot/SpringApplication/#) 。
- 然后,调用 `SpringApplication#run(Class<?> primarySource, String... args)` 方法,运行 Spring 应用。详细解析,见 [「2.2 run」](https://svip.iocoder.cn/Spring-Boot/SpringApplication/#) 。
## 2.1 构造方法
```
// SpringApplication.java
/**
* 资源加载器
*/
private ResourceLoader resourceLoader;
/**
* 主要的 Java Config 类的数组
*/
private Set<Class<?>> primarySources;
/**
* Web 应用类型
*/
private WebApplicationType webApplicationType;
/**
* ApplicationContextInitializer 数组
*/
private List<ApplicationContextInitializer<?>> initializers;
/**
* ApplicationListener 数组
*/
private List<ApplicationListener<?>> listeners;
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 初始化 initializers 属性
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 初始化 listeners 属性
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
```
- SpringApplication 的变量比较多,我们先只看构造方法提到的几个。
- `resourceLoader` 属性,资源加载器。可以暂时不理解,感兴趣的胖友,可以看看 [《【死磕 Spring】—— IoC 之 Spring 统一资源加载策略》](http://svip.iocoder.cn/Spring/IoC-load-Resource/?vip) 文章。
- `primarySources` 属性,主要的 Java Config 类的数组。在文初提供的示例,就是 MVCApplication 类。
- ```
webApplicationType
```
属性,调用
```
WebApplicationType#deduceFromClasspath()
```
方法,通过 classpath ,判断 Web 应用类型。
- 具体的原理是,是否存在指定的类,艿艿已经在 [WebApplicationType](https://github.com/YunaiV/spring-boot/blob/master/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/WebApplicationType.java) 上的方法添加了注释,直接瞅一眼就明白了。
- 这个属性,在下面的 `#createApplicationContext()` 方法,将根据它的值(类型),创建不同类型的 ApplicationContext 对象,即 Spring 容器的类型不同。
- ```
initializers
```
属性ApplicationContextInitializer 数组。
- 通过 `#getSpringFactoriesInstances(Class<T> type)` 方法,进行获得 ApplicationContextInitializer 类型的对象数组,详细的解析,见 [「2.1.1 getSpringFactoriesInstances」](https://svip.iocoder.cn/Spring-Boot/SpringApplication/#) 方法。
- 假设只在 Spring MVC 的环境下,`initializers` 属性的结果如下图:[![`initializers` 属性](07-Spring Boot 源码分析-SpringApplication.assets/01.jpg)](http://static.iocoder.cn/images/Spring-Boot/2021-01-07/01.jpg)`initializers` 属性
- ```
listeners
```
属性ApplicationListener 数组。
- 也是通过 `#getSpringFactoriesInstances(Class<T> type)` 方法,进行获得 ApplicationListener 类型的对象数组。
- 假设只在 Spring MVC 的环境下,`listeners` 属性的结果如下图:[![`listeners` 属性](07-Spring Boot 源码分析-SpringApplication.assets/02.jpg)](http://static.iocoder.cn/images/Spring-Boot/2021-01-07/02.jpg)`listeners` 属性
- `mainApplicationClass` 属性,调用 `#deduceMainApplicationClass()` 方法,获得是调用了哪个 `#main(String[] args)` 方法,代码如下:
```
// SpringApplication.java
private Class<?> deduceMainApplicationClass() {
try {
// 获得当前 StackTraceElement 数组
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
// 判断哪个执行了 main 方法
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
} catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
```
- 在文初的例子中,就是 MVCApplication 类。
- 这个 `mainApplicationClass` 属性,没有什么逻辑上的用途,主要就是用来打印下日志,说明是通过这个类启动 Spring 应用的。
### 2.1.1 getSpringFactoriesInstances
`#getSpringFactoriesInstances(Class<T> type)` 方法,获得指定类类对应的对象们。代码如下:
```
// SpringApplication.java
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// <1> 加载指定类型对应的,在 `META-INF/spring.factories` 里的类名的数组
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// <2> 创建对象们
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
// <3> 排序对象们
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
```
- ```
<1>
```
处,调用
```
SpringFactoriesLoader#loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader)
```
方法,加载指定类型对应的,在
```
META-INF/spring.factories
```
里的类名的数组。
- 在 [`META-INF/spring.factories`](https://github.com/YunaiV/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories) 文件中,会以 KEY-VALUE 的格式,配置每个类对应的实现类们。
- 关于 SpringFactoriesLoader 的该方法,我们就不去细看了。😈 很多时候,我们看源码的时候,不需要陷入到每个方法的细节中。非关键的方法,猜测到具体的用途后,跳过也是没问题的。
- `<2>` 处,调用 `#createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names)` 方法,创建对象们。代码如下:
```
// SpringApplication.java
/**
* 创建对象的数组
*
* @param type 父类
* @param parameterTypes 构造方法的参数类型
* @param classLoader 类加载器
* @param args 参数
* @param names 类名的数组
* @param <T> 泛型
* @return 对象的数组
*/
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<>(names.size()); // 数组大小,细节~
// 遍历 names 数组
for (String name : names) {
try {
// 获得 name 对应的类
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
// 判断类是否实现自 type 类
Assert.isAssignable(type, instanceClass);
// 获得构造方法
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
// 创建对象
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
} catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
```
- 比较简单,就不多做解释了。
- `<3>` 处,调用 `AnnotationAwareOrderComparator#sort(List<?> list)` 方法,排序对象们。例如说,类上有 [`@Order`](https://www.jianshu.com/p/8442d21222ef) 注解。
## 2.2 run
`#run(String... args)` 方法,运行 Spring 应用。代码如下:
> 艿艿:这是一个饱满的方法,所以逻辑比较多哈。
```
// SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
// <1> 创建 StopWatch 对象并启动。StopWatch 主要用于简单统计 run 启动过程的时长。
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// <2> 配置 headless 属性
configureHeadlessProperty();
// 获得 SpringApplicationRunListener 的数组,并启动监听
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// <3> 创建 ApplicationArguments 对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// <4> 加载属性配置。执行完成后,所有的 environment 的属性都会加载进来,包括 application.properties 和外部的属性配置。
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// <5> 打印 Spring Banner
Banner printedBanner = printBanner(environment);
// <6> 创建 Spring 容器。
context = createApplicationContext();
// <7> 异常报告器
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// <8> 主要是调用所有初始化类的 initialize 方法
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// <9> 初始化 Spring 容器。
refreshContext(context);
// <10> 执行 Spring 容器的初始化的后置逻辑。默认实现为空。
afterRefresh(context, applicationArguments);
// <11> 停止 StopWatch 统计时长
stopWatch.stop();
// <12> 打印 Spring Boot 启动的时长日志。
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// <13> 通知 SpringApplicationRunListener 的数组Spring 容器启动完成。
listeners.started(context);
// <14> 调用 ApplicationRunner 或者 CommandLineRunner 的运行方法。
callRunners(context, applicationArguments);
} catch (Throwable ex) {
// <14.1> 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
// <15> 通知 SpringApplicationRunListener 的数组Spring 容器运行中。
try {
listeners.running(context);
} catch (Throwable ex) {
// <15.1> 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
```
- `<1>` 处,创建 StopWatch 对象,并调用 `StopWatch#run()` 方法来启动。StopWatch 主要用于简单统计 run 启动过程的时长。
- `<2>` 处,配置 headless 属性。这个逻辑,可以无视,和 AWT 相关。
- `<3>` 处,调用 `#getRunListeners(String[] args)` 方法,获得 SpringApplicationRunListener 数组,并启动监听。代码如下:
```
// SpringApplication.java
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
```
- 此处的 `listeners` 变量,如下图所示:[![`listeners` 属性](07-Spring Boot 源码分析-SpringApplication.assets/03.jpg)](http://static.iocoder.cn/images/Spring-Boot/2021-01-07/03.jpg)`listeners` 属性
- 注意噢,此时是 SpringApplication**Run**Listener ,而不是我们看到 `listeners` 的 ApplicationListener 类型。详细的,我们在 [「3. SpringApplicationRunListeners」](https://svip.iocoder.cn/Spring-Boot/SpringApplication/#) 中,在详细解析。
- `<4>` 处,调用 `#prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments)` 方法,加载属性配置。执行完成后,所有的 environment 的属性都会加载进来,包括 `application.properties` 和外部的属性配置。详细的,胖友先一起跳到 [「2.2.1 prepareEnvironment」](https://svip.iocoder.cn/Spring-Boot/SpringApplication/#) 中。
- `<5>` 处,调用 `#printBanner(ConfigurableEnvironment environment)` 方法,打印 Spring Banner 。效果如下:
```
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot ::
```
- 具体的代码实现,就先不分析了。感兴趣的胖友,自己去瞅瞅。
- `<6>` 处,调用 `#createApplicationContext()` 方法,创建 Spring 容器。详细解析,见 [「2.2.2 createApplicationContext」](https://svip.iocoder.cn/Spring-Boot/SpringApplication/#) 。
- ```
<7>
```
处,通过
```
#getSpringFactoriesInstances(Class<T> type)
```
方法,进行获得 SpringBootExceptionReporter 类型的对象数组。SpringBootExceptionReporter ,记录启动过程中的异常信息。
- 此处,`exceptionReporters` 属性的结果如下图:[![`listeners` 属性](07-Spring Boot 源码分析-SpringApplication.assets/04.jpg)](http://static.iocoder.cn/images/Spring-Boot/2021-01-07/04.jpg)`listeners` 属性
- 关于 SpringBootExceptionReporter ,感兴趣的胖友,自己研究先。
- `<8>` 处,调用 `#prepareContext(...)` 方法,主要是调用所有初始化类的 `#initialize(...)` 方法。详细解析,见 [「2.2.3 prepareContext」](https://svip.iocoder.cn/Spring-Boot/SpringApplication/#) 。
- `<9>` 处,调用 ``#refreshContext(ConfigurableApplicationContext context)` 方法,启动(刷新) Spring 容器。详细解析,见 [「2.2.4 refreshContext」](https://svip.iocoder.cn/Spring-Boot/SpringApplication/#) 。
- `<10>` 处,调用 `#afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args)` 方法,执行 Spring 容器的初始化的后置逻辑。**默认实现为空**。代码如下:
```
// SpringApplication.java
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
```
- `<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 异常。
### 2.2.1 prepareEnvironment
> 艿艿:这个方法,大体看下即可。
`#prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments)` 方法,加载属性配置。代码如下:
```
// SpringApplication.java
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
// Create and configure the environment
// <1> 创建 ConfigurableEnvironment 对象,并进行配置
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
// <2> 通知 SpringApplicationRunListener 的数组,环境变量已经准备完成。
listeners.environmentPrepared(environment);
// <3> 绑定 environment 到 SpringApplication 上
bindToSpringApplication(environment);
// <4> 如果非自定义 environment ,则根据条件转换
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
// <5> 如果有 attach 到 environment 上的 MutablePropertySources ,则添加到 environment 的 PropertySource 中。
ConfigurationPropertySources.attach(environment);
return environment;
}
```
- `<1>` 处,调用 `#getOrCreateEnvironment()` 方法,创建 ConfigurableEnvironment 对象。代码如下:
```
// SpringApplication.java
private ConfigurableEnvironment getOrCreateEnvironment() {
// 已经存在,则进行返回
if (this.environment != null) {
return this.environment;
}
// 不存在,则根据 webApplicationType 类型,进行创建。
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
```
- 根据 `webApplicationType` 类型,会创建不同类型的 ConfigurableEnvironment 对象。
- 例如说Servlet 需要考虑 `<servletContextInitParams />` 和 `<servletConfigInitParams />` 等配置参数。
- `<1>` 处,调用 `#configureEnvironment(ConfigurableEnvironment environment, String[] args)` 方法,配置 `environment` 变量。代码如下:
```
// SpringApplication.java
/**
* 是否添加共享的 ConversionService
*/
private boolean addConversionService = true;
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
// <1.1> 设置 environment 的 conversionService 属性
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
// <1.2> 增加 environment 的 PropertySource 属性源
configurePropertySources(environment, args);
// <1.3> 配置 environment 的 activeProfiles 属性
configureProfiles(environment, args);
}
```
- `<1.1>` 处,设置 `environment` 的 `conversionService` 属性。可以暂时无视。感兴趣的胖友,可以看看 [《【死磕 Spring】—— 环境 & 属性PropertySource、Environment、Profile》](http://svip.iocoder.cn/Spring/PropertySource-and-Environment-and-Profile/)
- `<1.2>` 处,增加 `environment` 的 PropertySource 属性源。代码如下:
```
// SpringApplication.java
/**
* 是否添加 JVM 启动参数
*/
private boolean addCommandLineProperties = true;
/**
* 默认的属性集合
*/
private Map<String, Object> defaultProperties;
protected void configurePropertySources(ConfigurableEnvironment environment,
String[] args) {
MutablePropertySources sources = environment.getPropertySources();
// 配置的 defaultProperties
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
// 来自启动参数的
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) { // 已存在,就进行替换
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(new SimpleCommandLinePropertySource(
"springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
} else { // 不存在,就进行添加
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
```
- 代码上可以看出,可以根据配置的 `defaultProperties`、或者 JVM 启动参数,作为附加的 PropertySource 属性源。
- `<1.3>` 处,配置 `environment` 的 `activeProfiles` 属性。代码如下:
```
// SpringApplication.java
/**
* 附加的 profiles 的数组
*/
private Set<String> additionalProfiles = new HashSet<>();
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
environment.getActiveProfiles(); // ensure they are initialized 保证已经被初始化
// But these ones should go first (last wins in a property key clash)
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
// 设置 activeProfiles
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
```
- 不了解 Profile 的胖友,可以看看 [《Spring Boot 激活 profile 的几种方式》](https://my.oschina.net/u/1469495/blog/1522784) 文章。
- `<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 中。这块逻辑,也可以先无视。
### 2.2.2 createApplicationContext
`#createApplicationContext()` 方法,创建 Spring 容器。代码如下:
```
// SpringApplication.java
/**
* The class name of application context that will be used by default for non-web
* environments.
*/
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
/**
* The class name of application context that will be used by default for web
* environments.
*/
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
/**
* The class name of application context that will be used by default for reactive web
* environments.
*/
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
// 根据 webApplicationType 类型,获得 ApplicationContext 类型
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
} catch (ClassNotFoundException ex) {
throw new IllegalStateException("Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex);
}
}
// 创建 ApplicationContext 对象
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
```
- 根据 `webApplicationType` 类型,获得对应的 ApplicationContext 对象。
### 2.2.3 prepareContext
`#prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner)` 方法,准备 ApplicationContext 对象,主要是初始化它的一些属性。代码如下:
```
// SpringApplication.java
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// <1> 设置 context 的 environment 属性
context.setEnvironment(environment);
// <2> 设置 context 的一些属性
postProcessApplicationContext(context);
// <3> 初始化 ApplicationContextInitializer
applyInitializers(context);
// <4> 通知 SpringApplicationRunListener 的数组Spring 容器准备完成。
listeners.contextPrepared(context);
// <5> 打印日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
// <6> 设置 beanFactory 的属性
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
// <7> 加载 BeanDefinition 们
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
// <8> 通知 SpringApplicationRunListener 的数组Spring 容器加载完成。
listeners.contextLoaded(context);
}
```
- 这个方法,还是蛮长的,主要是给 `context` 的属性做赋值,以及 ApplicationContextInitializer 的初始化。
- `<1>` 处,设置 `context` 的 `environment` 属性。
- `<2>` 处,调用 `#postProcessApplicationContext(ConfigurableApplicationContext context)` 方法,设置 `context` 的一些属性。代码如下:
```
// SpringApplication.java
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
}
}
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
```
- `<3>` 处,调用 `#applyInitializers(ConfigurableApplicationContext context)` 方法,初始化 ApplicationContextInitializer 。代码如下:
```
// SpringApplication.java
protected void applyInitializers(ConfigurableApplicationContext context) {
// 遍历 ApplicationContextInitializer 数组
for (ApplicationContextInitializer initializer : getInitializers()) {
// 校验 ApplicationContextInitializer 的泛型非空
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
// 初始化 ApplicationContextInitializer
initializer.initialize(context);
}
}
```
- 遍历 ApplicationContextInitializer 数组,逐个调用 `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)
2019-01-28 17:53:40.028 INFO 21846 --- [ main] c.iocoder.springboot.mvc.MVCApplication : The following profiles are active: prod
```
- 具体的方法逻辑,胖友自己瞅瞅哈。
- `<6>` 处,设置 `beanFactory` 的属性。
- `<7>` 处,调用 `#load(ApplicationContext context, Object[] sources)` 方法,加载 BeanDefinition 们。代码如下:
```
// SpringApplication.java
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
// <1> 创建 BeanDefinitionLoader 对象
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
// <2> 设置 loader 的属性
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
// <3> 执行 BeanDefinition 加载
loader.load();
}
```
- `<1>` 处,调用 `#getBeanDefinitionRegistry(ApplicationContext context)` 方法,创建 BeanDefinitionRegistry 对象。代码如下:
```
// SpringApplication.java
private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
if (context instanceof BeanDefinitionRegistry) {
return (BeanDefinitionRegistry) context;
}
if (context instanceof AbstractApplicationContext) {
return (BeanDefinitionRegistry) ((AbstractApplicationContext) context)
.getBeanFactory();
}
throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
}
```
- 关于 BeanDefinitionRegistry 类,暂时不需要深入了解。感兴趣的胖友,可以看看 [《【死磕 Spring】—— IoC 之 BeanDefinition 注册表BeanDefinitionRegistry》](http://svip.iocoder.cn/Spring/IoC-BeanDefinitionRegistry/) 文章。
- `<1>` 处,调用 `#createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources)` 方法,创建 `org.springframework.boot.BeanDefinitionLoader` 对象。关于它,后续的文章,详细解析。
- `<2>` 处,设置 `loader` 的属性。
- `<3>` 处,调用 `BeanDefinitionLoader#load()` 方法,执行 BeanDefinition 加载。关于这一块,胖友感兴趣,先简单看看 [《【死磕 Spring】—— IoC 之加载 BeanDefinition》](http://svip.iocoder.cn/Spring/IoC-load-BeanDefinitions/) 文章。
- `<8>` 处,调用 `SpringApplicationRunListeners#contextLoaded(ConfigurableApplicationContext context)` 方法,通知 SpringApplicationRunListener 的数组Spring 容器加载完成。
### 2.2.4 refreshContext
`#refreshContext(ConfigurableApplicationContext context)` 方法,启动(刷新) Spring 容器。代码如下:
```
// SpringApplication.java
/**
* 是否注册 ShutdownHook 钩子
*/
private boolean registerShutdownHook = true;
private void refreshContext(ConfigurableApplicationContext context) {
// <1> 开启刷新Spring 容器
refresh(context);
// <2> 注册 ShutdownHook 钩子
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
```
- `<1>` 处,调用 `#refresh(ApplicationContext applicationContext)` 方法开启刷新Spring 容器。代码如下:
```
// SpringApplication.java
protected void refresh(ApplicationContext applicationContext) {
// 断言,判断 applicationContext 是 AbstractApplicationContext 的子类
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
// 启动(刷新) AbstractApplicationContext
((AbstractApplicationContext) applicationContext).refresh();
}
```
- 调用
```
AbstractApplicationContext#refresh()
```
方法启动刷新Spring 容器。
- `AbstractApplicationContext#refresh()` 方法,胖友可以看看 [《【死磕 Spring】—— ApplicationContext 相关接口架构分析》](http://svip.iocoder.cn/Spring/ApplicationContext/) 文章。
- 这里,可以触发 Spring Boot 的自动配置的功能。关于这一块,我们会在下一篇文章,详细解析。
- `<2>` 处,调用 `ConfigurableApplicationContext#registerShutdownHook()` 方法,注册 ShutdownHook 钩子。这个钩子,主要用于 Spring 应用的关闭时,销毁相应的 Bean 们。
### 2.2.5 callRunners
`#callRunners(ApplicationContext context, ApplicationArguments args)` 方法,调用 ApplicationRunner 或者 CommandLineRunner 的运行方法。代码如下:
```
// SpringApplication.java
private void callRunners(ApplicationContext context, ApplicationArguments args) {
// <1> 获得所有 Runner 们
List<Object> runners = new ArrayList<>();
// <1.1> 获得所有 ApplicationRunner Bean 们
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// <1.2> 获得所有 CommandLineRunner Bean 们
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// <1.3> 排序 runners
AnnotationAwareOrderComparator.sort(runners);
// <2> 遍历 Runner 数组,执行逻辑
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
```
- `<1>` 处,获得所有 Runner 们,并进行排序。
- `<2>` 处,遍历 Runner 数组,执行逻辑。代码如下:
```
// SpringApplication.java
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
(runner).run(args);
} catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
(runner).run(args.getSourceArgs());
} catch (Exception ex) {
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
}
```
关于 Runner 功能的使用,可以看看 [《ApplicationRunner 接口》](https://www.kancloud.cn/letiantian/learn-spring-boot/697172) 和 [《CommandLineRunner 接口》](https://www.kancloud.cn/letiantian/learn-spring-boot/696203) 文档。
# 3. SpringApplicationRunListeners
`org.springframework.boot.SpringApplicationRunListeners` SpringApplicationRunListener 数组的封装。代码如下:
```
// SpringApplicationRunListeners.java
class SpringApplicationRunListeners {
private final Log log;
/**
* SpringApplicationRunListener 数组
*/
private final List<SpringApplicationRunListener> listeners;
SpringApplicationRunListeners(Log log,
Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
public void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
public void contextPrepared(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextPrepared(context);
}
}
public void contextLoaded(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextLoaded(context);
}
}
public void started(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
public void running(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
}
public void failed(ConfigurableApplicationContext context, Throwable exception) {
for (SpringApplicationRunListener listener : this.listeners) {
callFailedListener(listener, context, exception);
}
}
private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context, Throwable exception) {
try {
listener.failed(context, exception);
} catch (Throwable ex) {
if (exception == null) {
ReflectionUtils.rethrowRuntimeException(ex);
}
if (this.log.isDebugEnabled()) {
this.log.error("Error handling failed", ex);
} else {
String message = ex.getMessage();
message = (message != null) ? message : "no error message";
this.log.warn("Error handling failed (" + message + ")");
}
}
}
}
```
## 3.1 SpringApplicationRunListener
`org.springframework.boot.SpringApplicationRunListener` SpringApplication 运行的监听器接口。代码如下:
```
// SpringApplicationRunListener.java
public interface SpringApplicationRunListener {
/**
* Called immediately when the run method has first started. Can be used for very
* early initialization.
*/
void starting();
/**
* Called once the environment has been prepared, but before the
* {@link ApplicationContext} has been created.
* @param environment the environment
*/
void environmentPrepared(ConfigurableEnvironment environment);
/**
* Called once the {@link ApplicationContext} has been created and prepared, but
* before sources have been loaded.
* @param context the application context
*/
void contextPrepared(ConfigurableApplicationContext context);
/**
* Called once the application context has been loaded but before it has been
* refreshed.
* @param context the application context
*/
void contextLoaded(ConfigurableApplicationContext context);
/**
* The context has been refreshed and the application has started but
* {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
* ApplicationRunners} have not been called.
* @param context the application context.
* @since 2.0.0
*/
void started(ConfigurableApplicationContext context);
/**
* Called immediately before the run method finishes, when the application context has
* been refreshed and all {@link CommandLineRunner CommandLineRunners} and
* {@link ApplicationRunner ApplicationRunners} have been called.
* @param context the application context.
* @since 2.0.0
*/
void running(ConfigurableApplicationContext context);
/**
* Called when a failure occurs when running the application.
* @param context the application context or {@code null} if a failure occurred before
* the context was created
* @param exception the failure
* @since 2.0.0
*/
void failed(ConfigurableApplicationContext context, Throwable exception);
}
```
目前SpringApplicationRunListener 的实现类,只有 EventPublishingRunListener 类。
## 3.2 EventPublishingRunListener
`org.springframework.boot.context.event.EventPublishingRunListener` ,实现 SpringApplicationRunListener、Ordered 接口,将 SpringApplicationRunListener 监听到的事件,转换成对应的 SpringApplicationEvent 事件,发布到监听器们。
代码如下:
```
// EventPublishingRunListener.java
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
/**
* Spring 应用
*/
private final SpringApplication application;
/**
* 参数集合
*/
private final String[] args;
/**
* 事件广播器
*/
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
// 创建 SimpleApplicationEventMulticaster 对象
this.initialMulticaster = new SimpleApplicationEventMulticaster();
// 添加应用的监听器们,到 initialMulticaster 中
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
@Override
public int getOrder() {
return 0;
}
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
@Override // ApplicationEnvironmentPreparedEvent
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
@Override // ApplicationContextInitializedEvent
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}
@Override // ApplicationPreparedEvent
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}
@Override // ApplicationStartedEvent
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
}
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
}
@Override // ApplicationFailedEvent
public void failed(ConfigurableApplicationContext context, Throwable exception) {
ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
if (context != null && context.isActive()) {
// Listeners have been registered to the application context so we should
// use it at this point if we can
context.publishEvent(event);
} else {
// An inactive context may not have a multicaster so we use our multicaster to
// call all of the context's listeners instead
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
this.initialMulticaster.multicastEvent(event);
}
}
private static class LoggingErrorHandler implements ErrorHandler {
private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);
@Override
public void handleError(Throwable throwable) {
logger.warn("Error calling ApplicationEventListener", throwable);
}
}
}
```
- 代码比较简单,胖友自己瞅瞅就明白了。
- 通过这样的方式,可以很方便的将 SpringApplication 启动的各种事件,方便的修改成对应的 SpringApplicationEvent 事件。这样,我们就可以不需要修改 SpringApplication 的代码。或者说,我们认为 EventPublishingRunListener 是一个“**转换器**”。
关于 Spring Boot 的事件,可以看看 [《事件监听与发布》](https://www.kancloud.cn/letiantian/learn-spring-boot/712108) 文章。
# 666. 彩蛋
整块代码略微有点长,胖友一定一定一定自己调试下。总的来说,逻辑并不复杂,是吧?是吧!
参考和推荐如下文章:
- 快乐崇拜 [《Spring Boot 源码深入分析》](https://gitbook.cn/books/5a445f030173cb29d2041d61/index.html)
> 需要付费 3 块钱。你看,艿艿为了写好源码解析,还是去学习了下别人的博客。
- 老田 [《Spring Boot 2.0 系列文章(七)SpringApplication 深入探索》](http://www.54tianzhisheng.cn/2018/04/30/springboot_SpringApplication/)
- oldflame-Jm [《Spring boot源码分析-SpringApplication启动1](https://blog.csdn.net/jamet/article/details/77413189)
- dm_vincent
- [《[Spring Boot\] 1. Spring Boot 启动过程源码分析》](https://blog.csdn.net/dm_vincent/article/details/76735888)
- [《[Spring Boot\] 2. Spring Boot 启动过程定制化》](https://blog.csdn.net/dm_vincent/article/details/77151122)
- 一个努力的码农
> 设计 Spring Framework 的部分,他也写了一些~
>
> - [《spring boot 源码解析2-SpringApplication初始化》](https://blog.csdn.net/qq_26000415/article/details/78914752)
> - [《spring boot 源码解析3-SpringApplication#run》](https://blog.csdn.net/qq_26000415/article/details/78914803)
> - [《spring boot 源码解析4-SpringApplication#run第4步》](https://blog.csdn.net/qq_26000415/article/details/78914944)
> - [《spring boot 源码解析5-SpringApplication#run第5步》](https://blog.csdn.net/qq_26000415/article/details/78915135)
> - [《spring boot 源码解析6-SpringApplication#run第6步》](https://blog.csdn.net/qq_26000415/article/details/78915181)
> - [《spring boot 源码解析7-SpringApplication#run第7步》](https://blog.csdn.net/qq_26000415/article/details/78915189)
> - [《spring boot 源码解析8-SpringApplication#run第8步》](https://blog.csdn.net/qq_26000415/article/details/78915211)
> - [《spring boot 源码解析9-SpringApplication#run第9步》](https://blog.csdn.net/qq_26000415/article/details/78915221)
> - [《spring boot 源码解析10-SpringApplication#run第10-13步》](https://blog.csdn.net/qq_26000415/article/details/78915234)