大家好,我是小米,一个在代码里泡了九年的程序员。前几天去面试一家互联网公司,面试官笑眯眯地问我:“你了解 Tomcat 的 Container 架构吗?”
我心想,这问题挺常见的,属于那种“问不深就考概念、问深了就劝退”的经典类型。于是我开始讲——没想到,这一讲,就聊了快半个小时。
今天就把我当时的回答完整复盘给你听,保证听完后,你不再怕面试官问这个问题。
故事开头:Tomcat 是一座“服务城市”我特别喜欢把 Tomcat 比作一座城市。
在这座“城市”里,有各种基础设施:
Connector(连接器):它就像城市的“交通入口”,负责接待外来的请求(HTTP 或 AJP)。
Container(容器):它是城市的“核心区域”,负责处理这些请求、执行 Servlet、返回结果。
可以这么说,Connector 接收请求,Container 处理请求。
而 Container 的内部,其实是一个层层嵌套、各司其职的“小宇宙”。
如果把它比作一座行政体系,那它大概长这样:
Engine(发动机) → Host(主机) → Context(上下文) → Wrapper(包装器)
听起来有点抽象?别急,我们慢慢拆。
Engine:一切请求的总管Engine 是整个 Container 体系的顶层容器,就像一座城市的“市政府”。
当 Connector 把请求交给 Engine 时,Engine 要决定:
这个请求该交给哪台主机(Host)?
用哪个 Web 应用(Context)去处理?
最后哪个具体的 Servlet(Wrapper)来执行
Tomcat 默认只有一个 Engine,名字叫 Catalina(这名字是不是很熟?Tomcat 的主类就叫这个)。
Engine 内部会注册多个 Host,比如:
localhost
myapp.company.com
test.company.com
Engine 根据请求头里的 Host 值去匹配对应的 Host。
换句话说,Engine 是整个容器链的入口,负责路由请求到正确的主机。
Host:承载多个 Web 应用的主机每个 Host 代表一台虚拟主机,也就是一个域名或主机名。
比如我们访问:
http://localhost:8080/demo
这里的 localhost 对应的就是一个 Host。
Host 的职责是:
管理多个 Web 应用(Context)
为每个应用分配独立的 ClassLoader(类加载空间)
隔离不同应用间的资源
每个 Host 下可以部署多个 Context,例如:
/demo /shop /blog
Host 的核心思想是“多租户”——不同项目可以共存而互不干扰。
Context:Web 应用的灵魂如果说 Host 是一台服务器,那 Context 就是一款部署在服务器上的 Web 应用。
Context 是最重要的容器之一,它直接对应一个 Web 项目(比如 webapps/demo 目录)。
Context 的职责包括:
管理 Servlet、Filter、Listener 等组件;
负责加载 web.xml、初始化应用;
负责分发请求到具体的 Servlet。
举个例子,当我们访问 /demo/hello 时:
Engine 根据主机名找到 Host;
Host 根据 /demo 找到对应的 Context;
Context 再去定位具体的 Servlet(Wrapper)。
可以看到,Context 是实际业务处理的舞台,所有业务逻辑、配置、过滤器、拦截器都在这一级展开。
Wrapper:Servlet 的“外衣”Wrapper 是 Container 体系中最底层、最贴近业务的一层。
它包裹着具体的 Servlet,比如 HelloServlet 或 UserLoginServlet。
Wrapper 负责:
管理 Servlet 的生命周期(加载、初始化、销毁);
处理线程安全;
调用真正的 service() 方法。
当请求一路从 Engine → Host → Context → Wrapper 传递下来时,最终在 Wrapper 层执行具体的 Servlet。
所以,Wrapper 是执行请求的最后一站。
四层容器的层级关系总结我们用一个简单的结构来表示它:

这四层是典型的“组合模式”(Composite Pattern),每一层都是 Container 的子类,都实现了 org.apache.catalina.Container 接口。
Tomcat 用这个设计实现了一个可扩展、可递归的容器层级体系——上层管理下层,下层专注具体逻辑。
Container 的核心接口设计Tomcat 的 Container 架构其实高度面向接口设计,主要接口包括:
Container:所有容器的顶层接口;
Engine、Host、Context、Wrapper:四个具体子接口;
Pipeline / Valve:责任链机制,用于在请求处理前后执行拦截逻辑;
Lifecycle:生命周期管理接口。
当我们理解这些接口后,就能明白 Tomcat 的设计精髓:
它把请求的处理过程抽象成一个管道(Pipeline),让每个 Valve 都能插入处理逻辑。
这就像在高速公路上设置多个收费站、检查站,每个环节都能“拦一拦、看一看、做点事”。
Pipeline 与 Valve:Tomcat 的责任链魔法在每个容器中,都有一个 Pipeline(管道),里面装着一连串的 Valve(阀门)。
请求进入时,会依次流过这些 Valve,每个 Valve 可以做:
日志记录
权限验证
过滤器处理
性能统计
最终调用下一级容器
比如在 Context 层的 Pipeline 中,常见的一个 Valve 是 StandardContextValve,它的作用是:
找到请求对应的 Wrapper;
调用 Wrapper 里的 Servlet。
而在 Engine 层、Host 层,也都有各自的 Valve,比如:
ErrorReportValve:统一异常处理;
AccessLogValve:记录访问日志。
这个机制让 Tomcat 非常灵活,你可以轻松在任意层插入自定义逻辑。
请求在 Container 中的流转之旅让我们以一次完整的请求为例,看看它的旅程:
1、Connector 接收请求
监听 8080 端口;
解析 HTTP 协议;
生成 Request 和 Response 对象;
把它交给 Engine。
2、Engine 匹配 Host
根据请求头里的 Host 字段找到对应的虚拟主机。
3、Host 匹配 Context
根据 URI 前缀(如 /demo)找到对应的应用。
4、Context 匹配 Wrapper
根据 Servlet 映射规则找到目标 Servlet。
5、Wrapper 调用 Servlet
通过 Pipeline 调用 Servlet 的 service() 方法;
最终执行我们的业务逻辑;
返回响应。
整个流程,就像一场“层层转发的旅程”:
Connector → Engine → Host → Context → Wrapper → Servlet

别以为讲完层级关系就结束了,面试官很可能接着问:
“那 Container 和 Connector 之间怎么通信?”
答:通过 Mapper 映射组件。
Mapper 根据请求 URL、主机名、路径来定位目标容器(Host → Context → Wrapper)。
Connector 解析完请求后,会调用 Engine.getPipeline().getFirst().invoke(request, response),启动整个责任链。
“每层容器如何实现解耦?”
答:Tomcat 使用接口 + 事件驱动机制,每个容器都实现 Lifecycle 接口,可以独立启动、销毁、重新加载。这让热部署、应用隔离变得可行。
写在最后:Tomcat 的“优雅之处”当年第一次看 Tomcat 源码,我差点被绕晕。
但当我真正理解 Container 的设计后,才发现它的优雅之处在于:
用组合模式实现层级容器;
用责任链实现灵活拦截;
用生命周期接口实现可插拔模块;
用映射机制实现精准路由。
这不是“写一堆类”的架构,而是一个能应对上亿次请求的分层系统。
所以,当面试官问你“Tomcat 的 Container 架构”时,千万别只背诵那四层。要讲出它们为什么这样设计、怎么协作、能带来什么好处。
彩蛋:一句话总结送给你Connector 负责“接”,Container 负责“处”。
Container 体系分四层:Engine、Host、Context、Wrapper。
每层用 Pipeline + Valve 实现责任链,
这就是 Tomcat 的“容器心脏”。
END下次再有人问你这个问题,就别怕了。
你可以微微一笑,说:“Tomcat 的 Container,我太熟了——它其实是一场层层嵌套的优雅设计。”
如果你觉得这篇文章有点帮助,点个“在看”吧,我会继续更新 Java 面试题系列,用故事帮你吃透底层源码!