一个 Tomcat 中有一个 Server,每个 Server 中至少有一个 Service。一个 Service 由至少一个 Connector 与一个 Container 组成。
Service 的结构主要可以被分为两部分:Connector 与 Container。
一个 Connector 又由三部分组成:EndPoint, Processor, Adapter;
Container 用来封装和管理 Servlet,并处理逻辑过程。Container 被分为四种类型:
根据 Tomcat 的文件目录可以对照几个 Container 的内容:
由于 Container 是一个实现了 LifeCycle 的接口,LifeCycle 接口中包含了初始化 init(), start(), stop(), destroy() 方法(生命周期方法),所以 engine, host, context, wrapper 都需要实现自己的生命周期方法。 四种类型的 Container 是通过组合的方式进行调用的。具体关系见下文的 Tomcat 启动流程。
首先客户端发送请求,被 Connector 捕获,处理为 HTTP 请求,发送到 Container。Container 使用责任链进行处理; 责任链可以理解为 Engine, Host, Context, Wrapper 四条 Pipeline 串行后的一条链,传入的请求首先传入 EnginePipeline 的第一个 Engine,最后传到 WrapperPipeline 的最后一个 Wrapper(StandardWrapperValue)。 值得一提的是,执行到最后的 StandardWrapperValue 时,会创建一个 FilterChain 用于匹配我们创建的 Filter 和 Servlet。FilterChain 的作用是通过其 doFilter 方法依次调用:
经过这样一系列链式处理,请求处理完毕,即可将结果通过响应 Response 返回给客户端。
参考地址: 《Tomcat启动过程源码解读》
Tomcat 启动的时序图如下所示:
Tomcat 启动的入口类:org.apache.catalina.startup.Bootstrap#main;
在 main 方法中调用 bootstrap#init() 方法,其中通过反射的方式初始化 Catalina 类,然后调用 Catalina 的 start() 方法,Tomcat 开始启动:
public void start() {
if (getServer() == null) {
load();
}
if (getServer() == null) {
log.fatal("Cannot start server. Server instance is not configured.");
return;
}
long t1 = System.nanoTime();
// Start the new server
try {
getServer().start();
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
}
// Register shutdown hook
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);
// If JULI is being used, disable JULI's shutdown hook since
// shutdown hooks run in parallel and log messages may be lost
// if JULI's hook completes before the CatalinaShutdownHook()
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
false);
}
}
if (await) {
await();
stop();
}
}
Catalina#start() 方法中的调用流程:
Tomcat 启动过程中会调用 org.apache.catalina.core.StandardContext # listenterStart() 方法,方法过程中会调用 listener.contextInitialized(event) 方法,其中的 event 类型是 ServletContextEvent。即 Tomcat 在启动时,会发送容器初始化事件。 通常在使用 Spring MVC 进行 web 部署时,web.xml 里面经常配置的一个监听器 ContextLoaderListenr,如下所示:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
这个 ContextLoaderListener 就在这时候监听到该事件,于是配合 web.xml 中其他的配置项,执行 Spring 容器和 SpringMVC 容器的初始化。其他配置项如下:
<context-param>
<!-- 用来指定 Spring 需要加载的配置文件 -->
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcherServlet-servlet.xml</param-value>
</context-param>
<!-- Spring MVC -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
配合 ContextLoaderListener 一起使用的,经常是 context-param,用来指定 Spring 需要加载的配置文件;而下面的 servlet 指定了 DispatcherServlet 作为具体的 Servlet 实现类,DispatcherServlet 是 SpringMVC 的核心调度器。dispatcherServlet-servlet.xml 中存储了配置 SpringMVC 的视图解析器等信息。
回到前面调用 ContextLoaderListener#contextInitialized 方法,观察源码可知主要的方法实现还是在 ContextLoader#initWebApplicationContext() 方法中。该方法的主要功能,就是创建一个 WebApplicationContext,并将其存入Tomcat WEB 应用的唯一全局上下文环境 ServletContext 中。