Spring Boot 启动类推断机制解析
<|begin▁of▁sentence|># 1. 概述
本文,我们来分享 [《精尽 Spring Boot 源码分析 —— SpringApplication》](http://svip.iocoder.cn/Spring-Boot/SpringApplication/) 的**初始化**的源码解析。
在 SpringApplication 的构造方法中,会进行一些初始化的工作。虽然说,它没有向 Spring 容器中添加 Bean ,但是**初始化**的过程,也是非常重要的。代码如下:
```java
// SpringApplication.java
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources. See {@link #run(String...)} for details
* on how the application is bootstrapped.
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// <1> 推断 Web 应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// <2> 设置 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// <3> 设置 ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// <4> 推断主应用类
this.mainApplicationClass = deduceMainApplicationClass();
}
```
- `<1>` 处,调用 `WebApplicationType#deduceFromClasspath()` 方法,推断 Web 应用类型。详细解析,见 [「2. 推断 Web 应用类型」](http://svip.iocoder.cn/Spring-Boot/SpringApplication/#) 。
- `<2>` 处,设置 ApplicationContextInitializer 数组。详细解析,见 [「3. 设置 ApplicationContextInitializer」](http://svip.iocoder.cn/Spring-Boot/SpringApplication/#) 。
- `<3>` 处,设置 ApplicationListener 数组。详细解析,见 [「4. 设置 ApplicationListener」](http://svip.iocoder.cn/Spring-Boot/SpringApplication/#) 。
- `<4>` 处,调用 `#deduceMainApplicationClass()` 方法,推断主应用类。详细解析,见 [「5. 推断主应用类」](http://svip.iocoder.cn/Spring-Boot/SpringApplication/#) 。
# 2. 推断 Web 应用类型
`WebApplicationType#deduceFromClasspath()` 方法,推断 Web 应用类型。代码如下:
```java
// WebApplicationType.java
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
public static WebApplicationType deduceFromClasspath() {
// 如果存在 REACTIVE 相关的类,则返回 REACTIVE
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// 如果不存在 SERVLET 相关的类,则返回 NONE
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// 如果存在 SERVLET 相关的类,则返回 SERVLET
return WebApplicationType.SERVLET;
}
```
- 根据不同的条件,返回不同的 WebApplicationType 枚举对象。
- WebApplicationType 枚举,代码如下:
```java
// WebApplicationType.java
public enum WebApplicationType {
/**
* The application should not run as a web application and should not start an
* embedded web server.
*/
NONE,
/**
* The application should run as a servlet-based web application and should start an
* embedded servlet web server.
*/
SERVLET,
/**
* The application should run as a reactive web application and should start an
* embedded reactive web server.
*/
REACTIVE;
}
```
- 一共有三种类型,NONE、SERVLET、REACTIVE 。
# 3. 设置 ApplicationContextInitializer
在 SpringApplication 的构造方法中,会调用 `#getSpringFactoriesInstances(Class type)` 方法,获得 ApplicationContextInitializer 类型的数组。代码如下:
```java
// SpringApplication.java
private Collection getSpringFactoriesInstances(Class type) {
return getSpringFactoriesInstances(type, new Class>[] {});
}
private Collection getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// <1> 加载指定类型对应的,在 `META-INF/spring.factories` 里的类名的数组
Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// <2> 创建对象
List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// <3> 排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
```
- `<1>` 处,调用 `SpringFactoriesLoader#loadFactoryNames(Class> factoryClass, ClassLoader classLoader)` 方法,加载指定类型对应的,在 `META-INF/spring.factories` 里的类名的数组。
- 在 Spring Boot 中,`META-INF/spring.factories` 配置如下:
```properties
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
```
- 关于 SpringFactoriesLoader 的源码解析,可见 [《【死磕 Spring】—— IoC 之加载 BeanDefinition》](http://svip.iocoder.cn/Spring/IoC-load-BeanDefinitions/#3-2-2-loadFactoryNames) 的 [「3.2.2 loadFactoryNames」](http://svip.iocoder.cn/Spring-Boot/SpringApplication/#) 小节。
- `<2>` 处,调用 `#createSpringFactoriesInstances(Class type, Class>[] parameterTypes, ClassLoader classLoader, Object[] args, Set names)` 方法,创建对象数组。代码如下:
```java
// SpringApplication.java
private List createSpringFactoriesInstances(Class type, Class>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set names) {
List instances = new ArrayList<>(names.size());
for (String name : names) {
try {
Class> instanceClass = ClassUtils.forName(name, classLoader);
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)` 方法,排序对象数组。
- 关于 AnnotationAwareOrderComparator 的排序,可见 [《【死磕 Spring】—— @Order 和 Ordered》](http://svip.iocoder.cn/Spring/Order-Ordered/) 。
- 创建完 ApplicationContextInitializer 对象数组后,会调用 `#setInitializers(Collection extends ApplicationContextInitializer>> initializers)` 方法,进行设置。代码如下:
```java
// SpringApplication.java
public void setInitializers(Collection extends ApplicationContextInitializer>> initializers) {
this.initializers = new ArrayList<>();
this.initializers.addAll(initializers);
}
```
- 设置到 `initializers` 属性中。
# 4. 设置 ApplicationListener
在 SpringApplication 的构造方法中,会调用 `#getSpringFactoriesInstances(Class type)` 方法,获得 ApplicationListener 类型的数组。
- 具体的逻辑,和 [「3. 设置 ApplicationContextInitializer」](http://svip.iocoder.cn/Spring-Boot/SpringApplication/#) 是一致的。
- 在 Spring Boot 中,`META-INF/spring.factories` 配置如下:
```properties
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceApplicationListener
```
- 创建完 ApplicationListener 对象数组后,会调用 `#setListeners(Collection extends ApplicationListener>> listeners)` 方法,进行设置。代码如下:
```java
// SpringApplication.java
public void setListeners(Collection extends ApplicationListener>> listeners) {
this.listeners = new ArrayList<>();
this.listeners.addAll(listeners);
}
```
- 设置到 `listeners` 属性中。
# 5. 推断主应用类
`#deduceMainApplicationClass()` 方法,推断主应用类。代码如下:
```java
// SpringApplication.java
private Class> deduceMainApplicationClass() {
try {
// 获得当前 StackTraceElement 数组
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
// 遍历每个 StackTraceElement ,通过方法名是否为 main ,
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
} catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
```
- 通过获得当前 StackTraceElement 数组,然后遍历每个 StackTraceElement ,通过方法名是否为 `"main"` ,来判断是哪个类。例如:
```
java.lang.RuntimeException
at org.springframework.boot.SpringApplication.deduceMainApplicationClass(SpringApplication.java:284)
at org.springframework.boot.SpringApplication.(SpringApplication.java:226)
at org.springframework.boot.SpringApplication.(SpringApplication.java:212)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)
at cn.iocoder.springboot.lab01.springbootdemo.SpringbootDemoApplication.main(SpringbootDemoApplication.java:15)
```
- 此时,`stackTraceElement.getClassName()` 方法,就会返回 `"cn.iocoder.springboot.lab01.springbootdemo.SpringbootDemoApplication"` 。
最新文章
- 专业技师揭秘:四轮定位关键参数与调整技巧全解析
- 汽车刹车液保养指南
- 800V高压快充技术解析:SiC器件与BMS系统的革新应用
- V2X技术推动智能汽车发展
- 汽车钣金变形登记证书:办理流程与重要性解析
- 电动汽车电量与续航关系
- 智能网联与自动驾驶:电动化时代的汽车技术革命
- 电动汽车三大核心技术突破:电池、电驱与充电新趋势
- V2X+高压快充+智能网联:三大技术重塑未来出行
- 汽车雨刮器保养全攻略:延长寿命确保雨天行车安全
- 水泵驱动汽车冷却系统高效运转
- 未来出行革命:新能源、自动驾驶与车联网重塑汽车产业
- 刹车失灵引发汽车安全警示
- 汽车散热器冷却系统高效降温保障引擎稳定运行
- 新手必学:5大驾驶技巧与雨天长途行车安全指南
- 拖车救援服务保障行车安全 汽车故障拖车快速响应 专业拖车服务护您爱车 拖车救援全天候待命 汽车拖车服务高效便捷
- 雨天行车安全指南:排气管涉水、雨刮器检查与轮胎气压维护
- 定期检查机油更换轮胎保养发动机
- 铝合金轻量化汽车车身设计
- 电动汽车三大核心技术突破:电池、充电与电控系统革新
