SpringApplication准备阶段
第二部分 推断Web应用类型和主引导类(Main Class)
推断Web应用类型
规则:
根据当前应用 ClassPath 中是否存在相关实现类来推断 Web 应用的类型,包括:
- Web Reactive: WebApplicationType.REACTIVE
- Web Servlet: WebApplicationType.SERVLET
- 非 Web: WebApplicationType.NONE
(本人使用的springboot版本是2.1.4.RELEASE)
在SpringApplication.java 中有getWebApplicationType() setWebApplicationType()
/**
* Returns the type of web application that is being run.//返回正在运行的web应用程序的类型。
* @return the type of web application
* @since 2.0.0
*/
public WebApplicationType getWebApplicationType() {
return this.webApplicationType;
}
/**
* Sets the type of web application to be run. If not explicitly set the type of web //设置要运行的web应用程序的类型。如果没有显式设置web的类型
* application will be deduced based on the classpath.//将根据类路径推导应用程序。
* @param webApplicationType the web application type
* @since 2.0.0
*/
public void setWebApplicationType(WebApplicationType webApplicationType) {
Assert.notNull(webApplicationType, "WebApplicationType must not be null");
this.webApplicationType = webApplicationType;
}
WebApplicationType 源码:
/**
* An enumeration of possible types of web application.
*
* @author Andy Wilkinson
* @author Brian Clozel
* @since 2.0.0
*/
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";
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";
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;
}
static WebApplicationType deduceFromApplicationContext(
Class<?> applicationContextClass) {
if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.SERVLET;
}
if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.REACTIVE;
}
return WebApplicationType.NONE;
}
private static boolean isAssignable(String target, Class<?> type) {
try {
return ClassUtils.resolveClassName(target, null).isAssignableFrom(type);
}
catch (Throwable ex) {
return false;
}
}
}
说明:
1)springboot-2.0的版本的SpringApplication.java里有个deduceWebApplicationType类,
但是在springboot2.1.4.RELEASE看不见这个类了,应该替换为WebApplicationType的这个deduceFromClasspath()
2.png
deduceFromClasspath() 这里有个判断条件:
当这个类DispatcherHandler类存在的时候(WEBFLUX_INDICATOR_CLASS = "org."+"springframework.web.reactive.DispatcherHandler";),
是webflux/reactive web里面的一个必然的类型,
并且另外的类DispatcherServlet不存在的时候( WEBMVC_INDICATOR_CLASS = "org.springframework."+"web.servlet.DispatcherServlet";)
会返回REACTIVE
这两个环境是不能共存的。
之前你会发现webmvc和webflux同时存在的时候,webflux没有被激活,原因就在这个地方。
2)foreach循环
当Servlet和ConfigurableWebApplicationContext都不存在的时候,
( SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };)
会返回非web类型
除此之外就会返回SERVLET
SERVLET存在的时候就会覆盖掉那2个
进行这样的推断是希望你少去配置。
3)你可以强制对类型进行关闭
例如:
/**
* {@link SpringApplication} 引导类
*/
public class SpringApplicationBootstrap {
public static void main(String[] args) {
//SpringApplication.run(ApplicationConfiguration.class,args);
Set<String> sources = new HashSet();
//配置class名称
sources.add(ApplicationConfiguration.class.getName());
SpringApplication springApplication = new SpringApplication();
springApplication.setSources(sources);
//配置这个。现在推断的类型是 web servlet类型,强制关闭掉就变成一个普通类型。springApplication.setWebApplicationType(WebApplicationType.NONE);
ConfigurableApplicationContext context =springApplication.run(args);
//System.out.println("Bean:" + context.getBean(ApplicationConfiguration.class));
}
@SpringBootApplication
public static class ApplicationConfiguration {
}
}
运行之后,发现主线程已经被kill掉了,因为,如果是web容器的话会有阻塞的过程,阻塞当前线程不被终止,你会接受不同的请求来响应这个处理,所以这里不是web的话就会变成一个普通的方式。
推断引导类(Main Class)
通常引导类Main Class是放在SpringApplication.run方法参数里面的
SpringApplication.run(DiveInSpringBootApplication.class, args);
我们下面的栗子就没有将Main Class放进,是通过ApplicationConfiguration放进去的
sources.add(ApplicationConfiguration.class.getName());
这个时候就不知道Main Class是哪一个,Main Class是如何进行推导的,
先来看源码吧
**SpringApplication.java中的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;
}
/**
* An element in a stack trace, as returned by {@link
* Throwable#getStackTrace()}. Each element represents a single stack frame.
* All stack frames except for the one at the top of the stack represent
* a method invocation. The frame at the top of the stack represents the
* execution point at which the stack trace was generated. Typically,
* this is the point at which the throwable corresponding to the stack trace
* was created.
*
* @since 1.4
* @author Josh Bloch
*/
public final class StackTraceElement implements java.io.Serializable {
// Normally initialized by VM (public constructor added in 1.5)
private String declaringClass;//当前的类
private String methodName;//当前的方法
private String fileName;
private int lineNumber;
说明:
这个实现是通过异常堆栈实现的,通过堆栈去判断
结合【栗子】再来具体看一下~
public class SpringApplicationBootstrap {
public static void main(String[] args) {
//SpringApplication.run(ApplicationConfiguration.class,args);
Set<String> sources = new HashSet();
//配置class名称
sources.add(ApplicationConfiguration.class.getName());
SpringApplication springApplication = new SpringApplication();
springApplication.setSources(sources);
springApplication.setWebApplicationType(WebApplicationType.NONE);
ConfigurableApplicationContext context =springApplication.run(args);
//System.out.println("Bean:" + context.getBean(ApplicationConfiguration.class));
}
@SpringBootApplication
public static class ApplicationConfiguration {
}
}
当执行这段代码的时候是启动一个线程,我们通过debug方式启动,跟踪一下,当跳到这里的时候,会有一个main的线程
3.png
我们可以看见当前线程的堆栈信息
堆栈信息说明:
1)0:当前执行方法的Thread.currentThread().getStackTrace()
2:)1:main方法---spring判断的就是main方法是不是这个
这里就已经推断出了Main Class是当前的类SpringApplicationBootstrap
我们再继续调试
会看到4个栈,其中一个栈就是SpringApplicationBootstrap
遍历的时候就会遍历到main方法里
这就是通过推断方式进行推导Main Class的。
网友评论