前面的文章中,我们介绍了Tomcat容器的关键组件和类加载器,但是现在的J2EE开发中更多的是使用SpringBoot内嵌的Tomcat容器,而不是单独安装Tomcat应用。那么Spring是怎么和Tomcat容器进行集成?Spring和Tomcat容器的生命周期是如何同步?本文会详细介绍Spring和Tomcat容器的集成。
SpringBoot与Tomcat
使用SpringBoot搭建一个网页,应该是很多Spring学习者入门的案例。我们只需要在pom添加Spring的web-starter依赖,并添加对应的Controller,一键启动之后就可以得到一个完整的Web应用示例。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.6.RELEASE</version>
</dependency>
@RestController
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@RequestMapping("/")
public String hello(){
return "hello";
}
}
既然是一个Web应用,那么该应用必定启动了对应的Servlet容器,常见的Servlet容器有Tomcat/Undertow/jetty/netty等,SpringBoot对这些容器都有集成。本文会重点分析SpringBoot是如何集成Tomcat容器的。
如何判断是不是Web应用
我们知道SpringBoot不一定以Web应用的形式运行,还可以以桌面程序的形式运行,那么SpringBoot在应用中如何判断应用是不是一个Web应用程序,是不是需要启动Tomcat容器的呢?
Spring容器在容器启动的时候,会调用WebApplicationType.deduceFromClasspath()
方法来推断当前的应用程序类型,从方法名字就可以看出,该方法是通过当前项目中的类来判断是不是Web项目的。以下为该方法的源码,当我们在项目中添加了spring-boot-starter-web
的依赖之后,项目路径中会包含webMvc的类,对应的Spring应用也会被识别为Web应用。
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;
}
根据应用类型创建应用
通过项目中包含类的类型,Spring可以判断出当前应用的类型,之后Spring就需要根据应用类型去创建对应的ApplicationContext。从下面的程序中可以看出来,对于我们关注的普通web应用,Spring会创建一个AnnotationConfigServletWebServerApplicationContext
。
ApplicationContextFactory DEFAULT = (webApplicationType) <