Tomcat Engine 原理深度解析
第 1 章:Tomcat Engine 的定义与定位
1.1 什么是 Engine?
在 Tomcat 的核心架构中,Engine 是整个 Servlet 容器的“心脏”,负责接收 Connector
传递过来的请求,并将其路由到合适的 Host → Context → Wrapper
,最终交给具体的 Servlet
执行。
可以把它类比成高速公路的交通枢纽:
Connector
像是高速路的入口收费站,负责接收外部车辆(请求)。Engine
是高速路的主干线,决定车辆应该走哪一条支路。Host
是具体的服务区,相当于一个虚拟主机(domain)。Context
是某个服务区里的商场(web 应用)。Wrapper
就是商场里的具体商铺(Servlet)。
1.2 Engine 的地位
在 Tomcat 的 Catalina 容器分层模型中:
Server
:Tomcat 的顶层容器,代表整个服务器。Service
:一个服务,可以包含多个Connector
和一个Engine
。Engine
:核心请求分发容器(每个 Service 仅有一个 Engine)。Host
:虚拟主机,支持多域名部署。Context
:Web 应用容器,一个 Host 下可以有多个 Context。Wrapper
:最底层,封装具体的 Servlet。
📌 重点:一个 Service 只能绑定一个 Engine,但 Engine 内部可以管理多个 Host,从而实现多站点、多租户。
1.3 配置示例
在 server.xml
中,我们能直观地看到 Engine 的定义:
<Service name="Catalina"><!-- Connector 定义省略 --><Engine name="Catalina" defaultHost="localhost"><Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"><Context path="" docBase="ROOT" reloadable="true" /></Host></Engine>
</Service>
解析:
Engine
绑定到 Service(如 Catalina Service)。defaultHost="localhost"
指定默认虚拟主机。Host
定义了一个虚拟主机,可绑定多个 Web 应用(Context)。
1.4 源码定位
在 Tomcat 10.x 源码中:
org.apache.catalina.Engine
是一个接口,继承自Container
。默认实现类为
org.apache.catalina.core.StandardEngine
。public interface Engine extends Container {// 获取默认Hostpublic String getDefaultHost();// 设置默认Hostpublic void setDefaultHost(String defaultHost); }
而 StandardEngine
则实现了核心逻辑:
public class StandardEngine extends ContainerBase implements Engine {private String defaultHost = null;@Overridepublic String getDefaultHost() {return (this.defaultHost);}@Overridepublic void setDefaultHost(String host) {this.defaultHost = host;}
}
可以看到:
Engine 其实就是一个 特殊的 Container,它的子容器是
Host
。它的关键属性就是
defaultHost
。
1.5 实际应用场景
假设你要在同一台 Tomcat 部署多个站点:
<Engine name="Catalina" defaultHost="www.example.com"><Host name="www.example.com" appBase="site1" /><Host name="blog.example.com" appBase="site2" />
</Engine>
这样,访问:
http://www.example.com/
→ 进入 site1http://blog.example.com/
→ 进入 site2
👉 Engine 在这里就扮演了 多租户隔离管理者 的角色。
✅ 小结:
Engine 是 Service 内的核心容器,负责将请求分发到正确的 Host/Context/Wrapper。
它是 Servlet 容器分层结构的中枢,既是容器(可以包含 Host),又是路由器(决定请求走向)。
配置层面上,Engine 通常只需要指定
defaultHost
,但在多站点、多域名部署时会非常关键。
第 2 章:Engine 在 Tomcat 分层架构中的作用
2.1 Tomcat 的容器分层模型
Tomcat 的核心是 Catalina 容器,它采用了分层容器架构(Container Hierarchy),从上到下依次是:
Server(顶层,代表整个 Tomcat 实例)
Service(一个服务,包含一个 Engine 和多个 Connector)
Engine(请求分发枢纽,一个 Service 只能有一个 Engine)
Host(虚拟主机,代表一个域名站点)
Context(Web 应用,一个 Host 下可以有多个 Context)
Wrapper(最底层,封装具体的 Servlet)
👉 这种分层设计,使 Tomcat 容器既清晰又灵活,方便扩展。
2.2 Engine 与其他组件的关系
与 Server 的关系
Server
是最顶层,包含多个Service
。每个
Service
必须有一个Engine
,因此 Engine 相当于 Server 与 Connector 的桥梁。
与 Service 的关系
Service
管理一组Connector
(负责协议/网络)和一个Engine
(负责容器)。请求流向:
Connector → Engine
。
与 Host 的关系
Engine 的直接子容器就是 Host。
一个 Engine 可以有多个 Host,用来支持多域名。
Host 内部再细分为 Context → Wrapper。
2.3 请求流转路径
结合一次 HTTP 请求来理解 Engine 的作用。假设用户访问:
http://blog.example.com/app/hello
请求流转大致如下:
Connector(如
Http11NioProtocol
)接收到请求。请求被传递到 Engine。
Engine 根据请求头中的
Host: blog.example.com
,找到对应的Host
。在
Host
内部,根据app
定位到Context
(Web 应用)。在
Context
内部,Mapper
会找到/hello
对应的Wrapper
(Servlet)。最终调用 Servlet 的
service()
方法执行逻辑。
📌 Engine 在这里的作用就是 承上启下:
上承 Connector 的网络输入
下启 Host/Context/Wrapper 的业务容器
2.4 源码结构剖析
在源码层面,Engine 继承自 Container
接口,而 Container
是所有容器(Engine/Host/Context/Wrapper)的顶层接口。
public interface Container {public String getName();public void setName(String name);public Container getParent();public void setParent(Container container);public Container[] findChildren();
}
而 Engine
是一个更具体的容器:
public interface Engine extends Container {public String getDefaultHost();public void setDefaultHost(String defaultHost);
}
👉 可以看出,Engine 的特殊性在于:
它的子容器是 Host
它必须有一个默认 Host
2.5 配置中的层级体现
来看一个 server.xml
的简化片段:
<Server port="8005" shutdown="SHUTDOWN"><Service name="Catalina"><Connector port="8080" protocol="HTTP/1.1"/><Engine name="Catalina" defaultHost="localhost"><Host name="localhost" appBase="webapps" /></Engine></Service>
</Server>
层次结构非常清晰:
Server → Service → Engine → Host
Connector 独立于容器,但它们和 Engine 一起组成 Service
2.6 类比理解
如果把 Tomcat 类比成一个“快递公司”:
Server:快递公司总部
Service:一个分公司(有自己的运输队和仓库)
Connector:运输队,负责接收外部快递
Engine:分公司总仓库,负责快递分拣
Host:城市分仓(北京、上海...)
Context:区县分仓(海淀区、浦东新区...)
Wrapper:具体的投递员(Servlet)
在这个比喻里,Engine 就是大仓库的分拣系统,决定一个快递(请求)应该送到哪一个城市分仓(Host)。
✅ 小结
Engine 在分层架构中处于 Service 与 Host 之间,是连接网络层(Connector)与应用层(Context/Wrapper)的关键枢纽。
Engine 的唯一性(每个 Service 只能有一个 Engine)保证了请求路由的稳定性。
它的本质作用是 请求分发和容器管理,承载了整个 Tomcat 容器的核心逻辑。
第 3 章:Engine 的生命周期管理(初始化、启动、停止)
3.1 生命周期接口
Tomcat 中所有容器组件(Engine、Host、Context、Wrapper)都实现了 Lifecycle
接口,用来统一管理组件的状态转换。
public interface Lifecycle {String BEFORE_INIT_EVENT = "before_init";String AFTER_INIT_EVENT = "after_init";String START_EVENT = "start";String BEFORE_START_EVENT = "before_start";String AFTER_START_EVENT = "after_start";String STOP_EVENT = "stop";String BEFORE_STOP_EVENT = "before_stop";String AFTER_STOP_EVENT = "after_stop";String DESTROY_EVENT = "destroy";void init() throws LifecycleException;void start() throws LifecycleException;void stop() throws LifecycleException;void destroy() throws LifecycleException;void addLifecycleListener(LifecycleListener listener);
}
📌 关键点:
每个阶段都会触发相应的事件(Event),供监听器(Listener)扩展。
Engine
作为ContainerBase
的子类,继承了Lifecycle
的完整实现。
3.2 生命周期阶段详解
Engine 生命周期主要分为以下几个阶段:
初始化(init)
初始化容器结构(加载子容器 Host)。
准备 Mapper(请求路径到容器的映射器)。
通知所有注册的
LifecycleListener
。
启动(start)
调用
startInternal()
,启动自身及子容器(Host → Context → Wrapper)。启动 Pipeline(责任链)中的 Valve。
触发
START_EVENT
。
停止(stop)
调用
stopInternal()
,依次停止 Pipeline 和子容器。回收资源(线程池、缓存、Session 管理器等)。
触发
STOP_EVENT
。
销毁(destroy)
释放底层资源。
通知所有监听器。
3.3 源码级解析(Tomcat 10.x)
以 StandardEngine
为例(继承自 ContainerBase
):
@Override
protected void startInternal() throws LifecycleException {// 设置状态为 STARTINGsetState(LifecycleState.STARTING);// 启动子容器(Host)super.startInternal();
}
可以看到,StandardEngine
并没有复杂逻辑,核心都在父类 ContainerBase
。
在 ContainerBase.startInternal()
中:
@Override
protected synchronized void startInternal() throws LifecycleException {// 启动 Pipeline(责任链)if (pipeline instanceof Lifecycle) {((Lifecycle) pipeline).start();}// 启动所有子容器(Host)for (Container child : findChildren()) {child.start();}setState(LifecycleState.STARTED);
}
👉 逻辑很清晰:
先启动 Pipeline(请求处理链)。
再递归启动所有子容器(Host → Context → Wrapper)。
同理,stopInternal()
过程正好反向:
先停止子容器
再停止 Pipeline
设置状态为 STOPPED
3.4 配置与生命周期的关系
在 server.xml
中修改 Engine 配置时,通常需要 重启 Tomcat 才能生效。原因就是 Engine 的生命周期被绑定到 Service → Server 的生命周期。
例如:
<Engine name="Catalina" defaultHost="localhost"><Host name="localhost" appBase="webapps" />
</Engine>
如果修改了 defaultHost
,必须重启 Tomcat,因为 Engine.startInternal()
只会在 Service 启动时执行一次。
3.5 生命周期监听器(Listener)
我们可以通过 LifecycleListener
扩展 Engine 的生命周期行为。
示例:自定义一个日志监听器
public class EngineLifecycleLogger implements LifecycleListener {@Overridepublic void lifecycleEvent(LifecycleEvent event) {System.out.println("Engine event: " + event.getType());}
}
在 server.xml
中挂载:
<Engine name="Catalina" defaultHost="localhost"><Listener className="com.example.EngineLifecycleLogger"/><Host name="localhost" appBase="webapps" />
</Engine>
这样在 Engine 初始化/启动/停止时都会输出日志。
3.6 实际应用场景
多站点动态部署
如果需要在运行中动态添加新的Host
,必须确保 Engine 已经start
,否则 Host 无法挂载。灰度发布
通过 LifecycleListener,可以在 Engine 启动时注册动态配置(如加载灰度路由规则)。资源回收
在stop
或destroy
阶段,可以自定义 Listener 释放数据库连接池、关闭 MQ 客户端等资源。
3.7 常见问题与排查
Engine 未启动导致 404
现象:Connector 正常,但所有请求返回 404。
原因:Engine 或子容器未成功启动(可能是
web.xml
配置错误)。解决:检查
catalina.out
,确认Engine.startInternal()
是否报错。
资源未释放导致内存泄漏
现象:Tomcat 重启后内存不断升高。
原因:Engine 停止时未正确调用
destroy()
,Listener 或线程池未释放。解决:实现自定义
LifecycleListener
,在 STOP_EVENT 阶段手动清理资源。
第 4 章:Host/Context/Wrapper 容器与 Engine 的关系
4.1 容器层级回顾
在 Tomcat 的容器分层模型中,Engine 作为 Service 内的核心容器,其子容器就是 Host。层级关系如下:
Engine└── Host (虚拟主机)└── Context (Web 应用)└── Wrapper (具体 Servlet)
👉 每一层容器都实现了 Container
接口,因此它们在管理子容器、查找子容器方面保持一致性。
4.2 Engine 与 Host 的关系
Engine 的子容器是 Host
每个 Host 对应一个虚拟主机(即域名),支持多站点部署。
Engine 通过
defaultHost
属性指定默认 Host,用来处理无法匹配的请求。
源码解析(Engine 添加 Host)
在ContainerBase
中,所有子容器统一管理:@Override public void addChild(Container child) {child.setParent(this); // 设置父容器children.put(child.getName(), child); // 存入子容器集合 }
对于
StandardEngine
来说,这里的 child 就是Host
。配置示例
<Engine name="Catalina" defaultHost="www.example.com"><Host name="www.example.com" appBase="site1"/><Host name="blog.example.com" appBase="site2"/> </Engine>
www.example.com
→ 映射到 site1 应用目录blog.example.com
→ 映射到 site2 应用目录
4.3 Host 与 Context 的关系
Host 的子容器是 Context
每个 Context 对应一个 Web 应用(通常是一个
WAR
包)。Host 负责应用的自动部署(
autoDeploy
、unpackWARs
参数控制)。
配置示例
<Host name="localhost" appBase="webapps" autoDeploy="true"><Context path="/app1" docBase="app1"/><Context path="/app2" docBase="app2"/> </Host>
http://localhost:8080/app1
→ 访问 app1 应用http://localhost:8080/app2
→ 访问 app2 应用
源码解析
StandardHost
继承ContainerBase
,其addChild()
方法确保子容器是Context
:@Override public void addChild(Container child) {if (!(child instanceof Context)) {throw new IllegalArgumentException("Child not a Context");}super.addChild(child); }
4.4 Context 与 Wrapper 的关系
Context 的子容器是 Wrapper
Context 表示一个 Web 应用,对应
web.xml
中的配置。Wrapper 封装了具体的 Servlet。
配置来源
在web.xml
或@WebServlet
注解中定义的 Servlet,都会被解析成 Wrapper:<servlet><servlet-name>HelloServlet</servlet-name><servlet-class>com.example.HelloServlet</servlet-class> </servlet> <servlet-mapping><servlet-name>HelloServlet</servlet-name><url-pattern>/hello</url-pattern> </servlet-mapping>
源码解析(Context 添加 Wrapper)
在StandardContext
中:@Override public void addChild(Container child) {if (!(child instanceof Wrapper)) {throw new IllegalArgumentException("Child not a Wrapper");}super.addChild(child); }
👉 这里的
Wrapper
就是对HelloServlet
的封装。
4.5 请求逐级下发机制
假设用户访问:
http://blog.example.com/app1/hello
请求路径在容器层级中的映射过程是:
Engine
根据
Host
头(blog.example.com
)找到对应的 Host。
Host
根据 URL 的 Context Path(
/app1
)找到对应的 Context。
Context
根据 Servlet 映射(
/hello
)找到对应的 Wrapper。
Wrapper
调用目标 Servlet 的
service()
方法处理请求。
📌 这里的 Mapper 组件起到关键作用,它在 Engine 层面维护了 Host → Context → Wrapper 的映射关系。
4.6 类比理解
我们继续用“快递公司”的类比:
Engine:总仓库(总分拣中心)
Host:城市仓库(北京仓、上海仓)
Context:区县仓库(海淀区仓、浦东区仓)
Wrapper:快递员(负责具体投递)
👉 请求就像一件快递,在 Engine 的大仓库里首先按城市分拣(Host),再按区县分拣(Context),最后分配给具体快递员(Wrapper)。
第 5 章:责任链模式(Pipeline-Valve)在 Engine 中的实现
5.1 责任链模式简介
责任链模式(Chain of Responsibility)是一种 行为设计模式,允许请求沿着处理链传递,直到某个处理器处理它。
在 Tomcat 中:
Pipeline → 责任链的容器
Valve → 责任链中的节点,每个 Valve 可以处理请求或将请求传递给下一个 Valve
优势:
灵活可扩展
每个处理器独立,实现关注点分离
支持动态添加/删除处理节点
5.2 Pipeline 与 Valve 的定义
Pipeline 接口
public interface Pipeline {public Valve getBasic();public void setBasic(Valve valve);public void addValve(Valve valve);public void invoke(Request request, Response response)throws IOException, ServletException; }
Valve 接口
public interface Valve {public void invoke(Request request, Response response, ValveContext context)throws IOException, ServletException; }
📌 核心思想:
Pipeline 维护一个 Valve 链
每个 Valve 调用
context.invokeNext()
将请求传递给下一个 Valve最后执行 Basic Valve(通常是 Servlet 调用)
5.3 Engine 的 Pipeline 实现
在 Tomcat 中,Engine、Host、Context 都实现了 Pipeline。
public class StandardEngine extends ContainerBase implements Engine, Pipeline {protected Valve basic = null;protected ArrayList<Valve> valves = new ArrayList<>();@Overridepublic void addValve(Valve valve) {valves.add(valve);((ContaineBase) this).pipeline = this;}@Overridepublic void invoke(Request request, Response response)throws IOException, ServletException {((ValveContextImpl) getPipelineContext()).invokeNext(request, response);}
}
valves:普通处理节点
basic:基础处理节点(最终处理,如将请求交给子容器)
5.4 Pipeline 执行流程
Connector 将请求传递给 Engine
Engine 的 Pipeline 调用第一个 Valve
Valve 执行自己的逻辑(如安全检查、日志记录、过滤器链等)
调用
context.invokeNext()
→ 下一个 Valve最终调用 Basic Valve → 将请求传递给 Host → Context → Wrapper → Servlet
5.5 配置示例
在 server.xml
中添加自定义 Valve:
<Engine name="Catalina" defaultHost="localhost"><Valve className="org.apache.catalina.valves.AccessLogValve"directory="logs"prefix="localhost_access_log"suffix=".txt"pattern="%h %l %u %t "%r" %s %b"/><Host name="localhost" appBase="webapps"/>
</Engine>
AccessLogValve
会在请求结束后记录日志可以自定义多个 Valve,形成责任链
5.6 自定义 Valve 示例
假设我们需要实现请求审计(打印请求 URL 与处理时间):
public class AuditValve implements Valve {@Overridepublic void invoke(Request request, Response response, ValveContext context)throws IOException, ServletException {long start = System.currentTimeMillis();System.out.println("Audit start: " + request.getRequestURI());// 调用下一个 Valvecontext.invokeNext(request, response);long duration = System.currentTimeMillis() - start;System.out.println("Audit end: " + request.getRequestURI() + ", duration=" + duration + "ms");}
}
添加到 Engine:
Engine engine = (Engine) service.getContainer(); engine.addValve(new AuditValve());
效果:每个请求都会经过 AuditValve 记录日志,然后传递给下一个 Valve。
5.7 Engine Pipeline 与子容器协作
Engine Pipeline 只处理 全局逻辑(如日志、安全、负载均衡)
Host/Context 的 Pipeline 处理 局部逻辑(如某个应用的过滤器、认证)
责任链是递归执行的:
Engine → Host → Context → Wrapper → Servlet
调用顺序举例:
Engine Valve1 → Engine BasicValve → Host Valve2 → Host BasicValve → Context Valve3 → Context BasicValve → Wrapper → Servlet
5.8 类比理解
继续用“快递公司”类比:
Engine:总分拣中心
Valve1:检查包裹重量
Valve2:检查地址有效性
Host:城市仓库
Valve:检查城市路由
Context:区县仓库
Valve:检查快递员可用性
Wrapper:快递员
实际投递
每个环节都是责任链节点,包裹沿链向下流转,最终送达目的地。
第 6 章:请求处理流程(Mapper 定位 → Pipeline 执行 → Servlet 调用)
6.1 请求处理的总体流程
当外部请求进入 Tomcat 时,处理流程大致分为三步:
Mapper 定位
Engine 根据请求的 Host 和 URI 找到对应的 Host/Context/Wrapper。
Pipeline 执行
请求沿着 Engine → Host → Context 的 Valve 链执行。
Servlet 调用
Wrapper 调用目标 Servlet 的
service()
方法,完成业务逻辑处理。
整个流程可概括为:
Connector → Engine (Pipeline) → Host → Context → Wrapper → Servlet
6.2 Connector 接收请求
以 Http11NioProtocol
为例:
SocketChannel channel = serverSocket.accept();
Request request = new Request(channel);
Response response = new Response(channel);
processor.process(request, response);
Connector 将网络请求封装成 Request 和 Response 对象。
请求被传递给 Engine 的 Pipeline 进行处理。
6.3 Engine Mapper 定位
Engine 内部有 Mapper 组件,用于将请求映射到对应的 Host/Context。
源码简化示例(StandardEngine
):
Host host = findHost(request.getServerName());
Context context = host.findChild(request.getContextPath());
Wrapper wrapper = context.findChild(request.getServletPath());
findHost()
:根据请求 Host 头找到对应的虚拟主机findChild()
:在 Host/Context 中查找对应子容器
📌 核心:Engine 只负责找到 Host,Context 和 Wrapper 进一步处理 URL 映射。
6.4 Pipeline 执行
请求定位完成后,通过 Pipeline 执行 Valve 链:
engine.getPipeline().invoke(request, response);
执行逻辑:
Engine Valve1 → Engine Valve2 → Engine BasicValve
Host Valve → Host BasicValve
Context Valve → Context BasicValve → Wrapper.invoke()
Engine BasicValve:将请求传给 Host
Host BasicValve:将请求传给 Context
Context BasicValve:将请求传给 Wrapper(Servlet)
6.5 Wrapper 调用 Servlet
Wrapper 是最底层的容器,封装了 Servlet 实例:
public void invoke(Request request, Response response) throws ServletException, IOException {Servlet servlet = loadServlet();servlet.service(request.getRequest(), response.getResponse());
}
loadServlet():延迟加载 Servlet,首次访问时实例化
service():调用具体的
doGet()
或doPost()
方法处理请求
6.6 文字版完整流程描述
假设用户请求 URL:
http://blog.example.com/app1/hello
流程如下:
Connector 接收 TCP 连接,封装 Request/Response
Engine Mapper 查找 Host:
blog.example.com
Engine Pipeline 执行全局 Valve(如日志、审计)
Host Mapper 查找 Context:
/app1
Host Pipeline 执行局部 Valve(如安全检查)
Context Mapper 查找 Wrapper:
/hello → HelloServlet
Context Pipeline 执行应用级 Valve(如请求计时)
Wrapper 调用 Servlet
service()
方法Servlet 处理业务逻辑,写入 Response
Response 返回给 Connector → 返回客户端
6.7 代码片段演示(模拟流程)
// Engine 层
engine.getPipeline().invoke(request, response);// Engine BasicValve
Host host = engine.findHost(request.getServerName());
host.getPipeline().invoke(request, response);// Host BasicValve
Context context = host.findChild(request.getContextPath());
context.getPipeline().invoke(request, response);// Context BasicValve
Wrapper wrapper = context.findChild(request.getServletPath());
wrapper.invoke(request, response);// Wrapper
Servlet servlet = wrapper.loadServlet();
servlet.service(request.getRequest(), response.getResponse());
这个流程展示了 Mapper 定位 + Pipeline 执行 + Servlet 调用 的逐级处理逻辑。
6.8 实际应用场景
多域名部署
Engine Mapper 根据 Host 头路由到不同虚拟主机
应用隔离
Host/Context 层提供独立 Pipeline 和 Wrapper,保证应用隔离
全局监控
Engine Pipeline 可以记录所有请求日志,Host/Context Pipeline 可做性能分析
6.9 小结
Engine 负责请求的 路由和全局处理
Host/Context 提供 局部处理和应用隔离
Wrapper 执行 具体业务逻辑
Mapper + Pipeline 是 Tomcat 请求处理链的核心机制
通过源码和文字描述,可以清晰理解请求从 TCP 到 Servlet 的完整路径
第 7 章:Engine 与 Servlet 规范的兼容性
7.1 Servlet 规范概览
Servlet 规范定义了 Java Web 应用的核心接口和行为,包括:
Servlet 接口:
javax.servlet.Servlet
或jakarta.servlet.Servlet
方法:
init() → service() → destroy()
生命周期管理
容器负责创建 Servlet 实例、调用初始化方法、调用
service()
方法处理请求、最终销毁
请求/响应对象
ServletRequest
/ServletResponse
提供 HTTP 请求信息和响应机制
过滤器链(Filter)
支持请求预处理和后处理
监听器(Listener)
支持事件感知(如 Session 创建、Context 初始化)
Tomcat Engine 必须支持这些规范,才能保证 Web 应用的可移植性。
7.2 Engine 如何支持 Servlet 生命周期
Engine 通过 Wrapper 承载具体 Servlet,每个 Wrapper 的行为严格遵循 Servlet 规范:
public void invoke(Request request, Response response)throws ServletException, IOException {Servlet servlet = loadServlet(); // 创建或获取 Servlet 实例servlet.service(request.getRequest(), response.getResponse()); // 调用 service
}
loadServlet()
:保证延迟加载,符合 Servlet 生命周期规范Engine/Context/Wrapper 负责管理 Servlet 的 初始化、并发访问、销毁
7.3 请求与响应对象兼容
Tomcat Engine 内部的 Request
和 Response
对象封装了 HTTP 协议细节,同时实现了 ServletRequest/ServletResponse 接口:
public class RequestFacade implements ServletRequest {private Request request; // 实际请求对象
}
通过 Facade 模式保护内部实现,保证 Web 应用无法直接访问底层容器
支持 Servlet 5.0 新特性,如 HTTP/2 协议、异步处理(
AsyncContext
)
7.4 Pipeline 与 Filter 机制的协作
Pipeline 是 Tomcat 的容器级责任链
Filter 是 Servlet 规范的应用级责任链
Engine 将请求处理分为两级:
Engine/Host/Context Pipeline:全局/局部容器逻辑
Filter Chain(Context → Wrapper):应用逻辑(如安全、日志、压缩)
示意流程:
Engine Pipeline → Host Pipeline → Context Pipeline → Filter Chain → Servlet.service()
这样既保证了容器级别功能,又兼容标准 Servlet 应用
7.5 支持异步请求(AsyncContext)
Servlet 3.0 及以上规范支持异步请求,Engine 在处理异步请求时也能兼容:
AsyncContext asyncContext = request.startAsync();
asyncContext.start(() -> {servlet.service(request, response);
});
Engine 不会阻塞请求线程
Pipeline 继续处理其他请求,提高并发能力
7.6 支持 Servlet 注解和 web.xml 配置
Engine 配置的 Context 会解析 Servlet 注解(@WebServlet、@WebFilter) 或 web.xml 文件,自动生成对应 Wrapper/Filter:
Context context = host.findChild("/app1");
context.addChild(new StandardWrapper("HelloServlet", "com.example.HelloServlet"));
自动完成 URL 映射
支持多 Servlet/Filter/Listener,保证与规范一致
第 8 章:Engine 的扩展性(自定义 Valve/Listener)
8.1 Engine 的扩展点概览
Tomcat Engine 提供了两个主要扩展点:
Valve(责任链节点)
插入请求处理责任链
可执行全局或应用级逻辑
示例:日志记录、权限校验、性能监控
Listener(事件监听器)
监听 Engine 生命周期事件
可执行初始化、销毁、启动、停止等操作
示例:资源初始化、动态配置加载
8.2 自定义 Valve
8.2.1 Valve 接口
public interface Valve {void invoke(Request request, Response response, ValveContext context)throws IOException, ServletException;
}
8.2.2 自定义示例:请求计时
public class TimingValve implements Valve {@Overridepublic void invoke(Request request, Response response, ValveContext context)throws IOException, ServletException {long start = System.currentTimeMillis();context.invokeNext(request, response); // 调用下一个 Valvelong duration = System.currentTimeMillis() - start;System.out.println("Request URI: " + request.getRequestURI() + ", Duration: " + duration + "ms");}
}
8.2.3 添加到 Engine
Engine engine = (Engine) service.getContainer();
engine.addValve(new TimingValve());
执行顺序:按照
addValve
的顺序形成责任链Basic Valve:最后执行,将请求传给 Host
8.3 自定义 Listener
8.3.1 LifecycleListener 接口
public interface LifecycleListener {void lifecycleEvent(LifecycleEvent event);
}
8.3.2 示例:Engine 启动日志
public class EngineStartupLogger implements LifecycleListener {@Overridepublic void lifecycleEvent(LifecycleEvent event) {if (Lifecycle.START_EVENT.equals(event.getType())) {System.out.println("Engine started at " + System.currentTimeMillis());}}
}
8.3.3 在 server.xml 中配置
<Engine name="Catalina" defaultHost="localhost"><Listener className="com.example.EngineStartupLogger"/><Host name="localhost" appBase="webapps"/>
</Engine>
当 Engine 启动时,会自动触发
lifecycleEvent
,输出启动日志