构造方法
下面是SpringApplication的构造方法:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 第一步:推断Web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 第二步:设置ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 第三步:设置ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 第四步:推断主类
this.mainApplicationClass = deduceMainApplicationClass();
}
从构造方法中可以看到,SpringApplication的构建过程主要包含三步:
第一步:推断Web应用类型
第二步:设置ApplicationContextInitializer
第三步:设置ApplicationListener
第四步:推断主类
下面我们就分别讲解一个这四个步骤。
第一步:推断Web应用类型
public enum WebApplicationType {
// 不需要在web容器的环境下运行,也就是普通的工程
NONE,
// 基于servlet的Web项目
SERVLET,
// 响应式web应用==reactive web Spring5版本的新特性
REACTIVE;
}
判断的方式也很简单:
- 如果Classpath中包含org.springframework.web.reactive.DispatcherHandler,并且不包含org.springframework.web.servlet.DispatcherServlet和org.glassfish.jersey.servlet.ServletContainer,则认为是REACTIVE类型;
- 如果不包含javax.servlet.Servlet或org.springframework.web.context.ConfigurableWebApplicationContext,则为NONE类型。
- 否则为SERVLET类型。
如果想要手动指定Web应用类型,可以通过下面的方式实现:
public static void main(String[] args) {
new SpringApplicationBuilder(HelloApplication.class)
.web(WebApplicationType.NONE)
.run(args);
}
第二步:设置ApplicationContextInitializer
设置ApplicationContextInitializer是通过SpringFactoriesLoader读取spring.factories文件的配置实现的,具体流程参见Spring Factories
在获取到对应的factory类名之后,会调用factory类默认的构造方法对其进行实例化。首先判断factoryName对应的类是否派生自目标类,如果是,则调用其默认的构造函数实例化,否则抛出IllegalArgumentException异常。
到这一步我们可以得到一些启示:如果我们想要创建一个jar包,并且想在Spring Boot项目启动时,自动完成jar包的一些初始工作,就可以在META-INF/spring.factories文件中配置我们自定义的factory类。这就是Spring starter的基本原理。
如果我们想监听Spring生命周期的相关事件,也可以实现自定义的ApplicationListener。
第三步:设置ApplicationListener
设置ApplicationListener的方式与上面相同。
第四步:推断主类
推断主类的实现代码如下:
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方法的类,即是主类
网友评论