SpringBoot
的 web
类型有三种,分别是,NONE
、SERVLET
和 REACTIVE
,定义在枚举 WebApplicationType
中,这三种类型分别代表了三种含义:
NONE
:不是一个web
应用,不需要启动内置的web
服务器;SERVLET
:基于servlet
的web
应用,需要启动一个内置的servlet
服务器;REACTIVE
:一个reactive
的web
应用,需要启动一个内置的reactive
服务器;
servlet和reactive区别
servlet:同步阻塞式
reactive:异步非阻塞式
上图我们可以清晰地区分出基于Servlet api和Reactive各自的技术栈结构,不过在最上层,Spring都给我们封装成了我们在SpringMVC中使用的基于注解的Web层api。
pom依赖
None web:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
Servlet web:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Reactive web:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
启动结果
None web
类型下,应用启动运行后就自动关闭了,并没有启动内置的 web
服务器,也没有监听任何端口。
Servlet web 类型下,启动了内置的 Tomcat Servlet
服务器,监听了 8080
端口,应用程序并不会像 None
类型一样,启动后就自动关闭。
Reactive web类型下,启动了内置的 Netty
服务器,并监听在 8080
端口上。
启动源码解析
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
applicationContextFactory有三个实现类:
根据WebApplicationType来判断进入哪个类的create方法。
webApplicationType初始化
public enum WebApplicationType {
NONE,
SERVLET,
REACTIVE;
private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{"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 WebApplicationType() {
}
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
return REACTIVE;
} else {
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
String className = var0[var2];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}
return SERVLET;
}
}
}
在当前类路径下面如果当 org.springframework.web.reactive.DispatcherHandler
存在而且 org.springframework.web.servlet.DispatcherServlet
和 org.glassfish.jersey.servlet.ServletContainer
都不存在的时候说明当前应用 web
类型为 Reactive
。
当 javax.servlet.Servlet
和 org.springframework.web.context.ConfigurableWebApplicationContext
任何一个不存在的时候,就说明当前应用是 None
类型非 web
应用。否则当前应用就为 Servlet
类型。
而我们再看这个 ClassUtils.isPresent()
方法,可以发现底层是通过 className
在类路径上加载对应的类,如果存在则返回 true
,如果不存在则返回 false
。
因此这也解释了为什么我们在 pom
文件中只要加入对应的依赖就可以直接得到相应的 web
类型了,因为当我们在 pom
中加入相应的依赖过后,类路径里面就存在了前面判断的对应的类,再通过 ClassUtils.isPresent()
就判断出来当前应用属于那种 web
类型了。
以servlet为例:
根据继承关系找到ServletWebServerApplicationContext类,其中onRefresh方法中调用了createWebServer方法
protected void onRefresh() {
super.onRefresh();
try {
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = this.getServletContext();
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
ServletWebServerFactory factory = this.getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
//根据ServletContextInitializer确定创建server的类型
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
createWebServer.end();
this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
} else if (servletContext != null) {
try {
this.getSelfInitializer().onStartup(servletContext);
} catch (ServletException var5) {
throw new ApplicationContextException("Cannot initialize servlet context", var5);
}
}
this.initPropertySources();
}
比如TomcatServletWebServerFactory中就创建了tomcat服务器
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Iterator var4 = this.serverLifecycleListeners.iterator();
while(var4.hasNext()) {
LifecycleListener listener = (LifecycleListener)var4.next();
tomcat.getServer().addLifecycleListener(listener);
}
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var8 = this.additionalTomcatConnectors.iterator();
while(var8.hasNext()) {
Connector additionalConnector = (Connector)var8.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
return this.getTomcatWebServer(tomcat);
}