QQ: 408365330
email: egojit@qq.com
springboot2.2.x源码学习-SpringApplication构造说起
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 2.1 设置资源加载器
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 2.2 判断webserver启动类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//2.3 设置Initializers
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//2.4 设置Listeners
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 2.5 设置主类也就是main函数所在的类
this.mainApplicationClass = deduceMainApplicationClass();
}
2.1 设置资源加载器
我们的springboot项目main函数中一般都是 SpringApplication.run(ProxyBootApplication.class, args);
所以第一个参数resourceLoader 是null;也就是使用系统默认的资源加载器
2.2 判断webserver启动类型
三种web服务器类型
- 1.REACTIVE
- 说明:reactive类型是netty实现的web服务器,其实本质上就是相应是编程风格,据说性能上面更优,springboot2以上才支持的
- 2.NONE
- 说明:不带任何web服务器,这个可以理解成普通的main程序,以前碰到一个需求就是:根据配置来决定是否开启接口和端口,看到这里我们可以实现这种需求;
- 3.SERVLET
- 说明:传统的servlet方式,只要是支持servlet标准的web服务器都支持,例如tomcat,jetty……;
springboot是如何判断选择那种类型的呢?通过
- 说明:传统的servlet方式,只要是支持servlet标准的web服务器都支持,例如tomcat,jetty……;
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;
}
第一步:
判断是否是reactive类型的web;通过扫描所有的classpath也就是引用的jar中包含了org.springframework.web.reactive.DispatcherHandler类 且
不能包含org.springframework.web.servlet.DispatcherServlet 类 且
不能保护 org.glassfish.jersey.servlet.ServletContainer类;
从以上三个条件我们看出两点
1.要使用reactive类型web我们的项目中只能包含spring-webflux 的jar包;也就是引用下面的pom,它保护了spring-webflux;而不能包含spring-webmvc;二选其一;否则默认使用的就是非响应式web开发,而是使用传统的servlet(这是什么就不多说,java web的基础
)
必须引用:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>xxxx</version>
</dependency>
不应该引用:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>xxxx</version>
</dependency>
第二步
判断只要 javax.servlet.Servlet 或者
org.springframework.web.context.ConfigurableWebApplicationContext两个类其中有一个不存在都不会以web服务启动;如果我们不希望springboot以web的方式启动破坏其中一个条件就行了
2.3 设置Initializers
重点是这个方法getSpringFactoriesInstances(ApplicationContextInitializer.class);这个方法的作用是从目录**META-INF/spring.factories的的文件中获取ApplicationContextInitializer类相关的配置;一下是spring-boot的jar包中META-INF/spring.factories文件关于ApplicationContextInitializer类的配置;当然其它的类在这个文件中这样配置也是类似的,例如下面 ApplicationListener,在这里我且成为springboot的factories机制
# 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.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
它们的作用后面的博客会一个一个介绍;这里我们知道springboot初始化了这样一个ApplicationContextInitializer实现类的列表就行了;读源码千万不要一步追求到位;先抓大后捡小
;到这里我们知道,如过我们希望给我们的项目添加自己的ApplicationContextInitializer,只要在自己的项目中新建META-INF/spring.factories,并且按照上面的格式填上内容
例如:
org.springframework.context.ApplicationContextInitializer=\
xxxxxApplicationContextInitializer
2.4 设置ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
通过springboot的factories机制从META-INF/spring.factories中读取ApplicationListener列表;和上面的ApplicationContextInitializer一样,如果我们要添加自定义的ApplicationListener,也只要新建一个META-INF/spring.factories文件并且配置上就行了;结合后面这些类的生命周期我们就知道如何在springboot启动前和springboot启动后干一些自己想干的事情了
2.5 设置主类也就是main函数所在的类
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;
}
deduceMainApplicationClass();这个方法逻辑就是便利调用堆栈,然后比对找到main函数所在的类;我这种方式很巧妙;以后我们要找某个方法所在的类也可以通过这种方式干;看上面的逻辑我们知道,我们的项目中是不允许有多个main存在的否则后面使用这个类的时候会出问题,毕竟它只是返回了第一个包含了main函数的类