SpringBoot编程思想 读书笔记 -- SpringApplication
《SpringBoot编程思想》书中关于SpringBoot2源码分析,版本使用的是:2.0.2.RELEASE
;
而本文中的源码分析,版本使用的是:2.1.6.RELEASE
,所以与书中会有少许差异;
1. SpringApplication 初始化阶段
分为 构造阶段
和 配置阶段
;
1.1 SpringApplication 构造阶段
平时我们使用的都是调用 SpringApplication 的 run() 方法,其实内部是包含了其构造方法的;
日常使用 SpringApplication 的案例:
@SpringBootApplication
public class MicroVideoApplication{
public static void main(String[] args) {
SpringApplication.run(MicroVideoApplication.class, args);
}
}
其 SpringApplication 内部 run() 方法的源码是:
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
可见最后还是调用了 SpringApplication 的构造方法的,返回的是一个 ApplicationContext ;
上诉日常用法中 SpringApplication.run(MicroVideoApplication.class, args) ,其中 MicroVideoApplication 类其实可以更换成任意其他类,只要此类上也使用 @SpringBootApplication 进行注解;如下所示:
public class MicroVideoApplication {
public static void main(String[] args) {
SpringApplication.run(BootStrapClass.class, args);
}
}
@SpringBootApplication
class BootStrapClass {
}
SpringApplication 构造过程:
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();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
根据其调用链路依次为:推断 Web 应用类型
、加载 Spring 应用上下文初始化器
、加载 Spring 应用事件监听器
和推导应用引导类
。
1.1.1. 推断 Web 应用类型
deduceFromClasspath()
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;
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";
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
// ...
}
deduceFromClasspath() 利用 ClassUtils#isPresent 方法依次判断 DispatcherHandler 、 ConfigurableWebApplicationContext 、 Servlet 和 DispatcherServlet 以及 ServletContainer 的存在性组合情况,从而推断 Web 应用类型:
(1)当 DispatcherHandler 存在,且 DispatcherServlet 和 ServletContainer 都不存在时;
推断出类型为:WebApplicationType.REACTIVE
,即 Reactive Web ;
(2)当 Servlet 和 ConfigurableWebApplicationContext 均不存在时;
推断出应用类型为:WebApplicationType.NONE
,即 非 Web 应用;
(3)否则,类型就为:WebApplicationType.SERVLET
,即 普通的 Web 应用;
1.1.2. 加载 Spring 应用上下文初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
分为两个动作:getSpringFactoriesInstances(Class) 和 setInitializers(Collection) ;
// 第一步
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
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
// 第二步
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>();
this.initializers.addAll(initializers);
}
第一步中,将返回所有 META-INF/spring.factories 资源中配置的 ApplicationContextInitializer 实现类名单;
# Application Context 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
当获取到 ApplicationContextInitializer 实现类名单后,调用 createSpringFactoriesInstances 方法初始化这些实现类;
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> 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;
}
注意:ApplicationContextInitializer 实现类必须存在默认构造器;
1.1.3. 加载 Spring 应用事件监听器
本过程的实现手段与“加载 Spring 应用上下文初始化器”基本一致,先执行 getSpringFactoriesInstances 方法,再设置实例集合,只不过初始化的对象类型从 ApplicationContextInitializer
变成了 ApplicationListener
;
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
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
# 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.LiquibaseServiceLocatorApplicationListener
1.1.4. 推导应用引导类
deduceMainApplicationClass()
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
该方法根据当前线程执行栈来判断栈中哪个类包含main方法,尽管这个方法的实现不够严谨,不过可以覆盖绝大多数以 Java 的标准 mian 方法引导的情况;
在 SpringApplication 的构造过程中, SpringApplication 的属性 Set<Class<?>> primarySources WebApplicationType webApplicationType 、 List
1.2 SpringApplication 配置阶段
构造阶段完成后,接下来是配置阶段;该阶段是可选的,不设置则会使用默认配置;
该阶段主要是用于调整或补充构造阶段的状态;
调整行为主要以 SpringApplication Setter 方法为代表,补充行为则以 add* 方法为主;可参考文档 23.3 Customizing SpringApplication
也可以使用 SpringApplicationBuilder ,作用相同,主要是提高 API 的便利性。
1.2.1 调整 SpringApplication 设置
官网文档案例:
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MySpringConfiguration.class);
app.setBannerMode(Banner.Mode.OFF);
app.run(args);
}
或
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
}