Tomcat Web 应用加载机制详解
Tomcat 作为主流的 Servlet 容器,其 Web 应用的加载过程是核心功能之一。这一过程主要由 StandardHost、HostConfig、StandardContext、ContextConfig、StandardWrapper 五个核心类协同完成,涵盖了从应用部署到初始化的全流程。本文将详细解析这一机制。
核心组件角色概述
在 Web 应用加载过程中,五个核心类的职责如下:
| 组件类 | 核心职责 |
|---|---|
| StandardHost | 作为虚拟主机容器,管理多个 Web 应用(StandardContext),提供加载入口。 |
| HostConfig | 监听 Host 生命周期事件,负责自动扫描部署目录、部署 / 卸载 Web 应用。 |
| StandardContext | 代表一个 Web 应用,负责应用的初始化、启动及资源管理。 |
| ContextConfig | 监听 StandardContext 事件,解析配置文件(如 web.xml),初始化 Servlet、Filter 等。 |
| StandardWrapper | 管理单个 Servlet 实例,负责 Servlet 的加载、初始化及生命周期控制。 |
StandardHost:Web 应用加载的入口
StandardHost 是虚拟主机的实现类,作为 Web 应用(StandardContext)的父容器,提供了两种 Web 应用加载入口:
1. 基于 server.xml 配置的静态加载
当在 server.xml 的 <Host> 标签中直接配置 <Context> 子元素时,Tomcat 会在启动时将其作为子容器添加到 Host 并启动。
配置示例:
1 | <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> |
特点:
- 需手动修改
server.xml,不适合动态部署。 reloadable="true"表示当应用类或配置文件变化时,自动重新加载。
2. StandardHost 启动加载流程
StandardHost 的启动过程(startInternal() 方法)是 Web 应用加载的触发点,关键步骤包括:
- 添加错误处理阀门:注册
ErrorReportValve,用于生成默认错误页面(当应用未配置错误页时)。 - 启动子组件:
- 启动集群组件(Cluster)和安全组件(Realm)。
- 启动
server.xml中配置的<Context>实例(静态加载的应用)。
- 触发部署事件:设置 Host 状态为
STARTING,触发START_EVENT事件,由 HostConfig 监听并执行自动部署。 - 启动后台任务:如集群同步、安全认证等周期性任务。
HostConfig:自动部署的核心
HostConfig 通过监听 Host 的生命周期事件,实现 Web 应用的自动部署、更新和卸载。其处理的核心事件包括 START_EVENT(启动时部署)、PERIODIC_EVENT(定时扫描更新)和 STOP_EVENT(停止时清理)。
1. START_EVENT:启动时部署应用
当 Host 启动且 deployOnStartup="true"(默认值)时,HostConfig 扫描部署目录,支持三种部署方式:
(1)Context 描述文件部署
通过独立的 XML 配置文件定义 Web 应用,存储路径为 $CATALINA_BASE/conf/<Engine名称>/<Host名称>(默认:conf/Catalina/localhost)。
示例:在上述目录创建 myApp.xml:
1 | <Context docBase="/path/to/myApp" path="/myApp" reloadable="false"> |
docBase:应用实际目录或 WAR 包路径。path:访问路径(为空时默认为文件名)。
(2)Web 目录部署
将 Web 应用目录(包含 WEB-INF、META-INF 等)直接复制到 Host 的 appBase 目录(默认 webapps),Tomcat 会自动识别并部署。
特点:
- 应用配置优先读取目录内的
META-INF/context.xml。 path由目录名决定(ROOT目录对应根路径/)。
(3)WAR 包部署
将应用打包为 WAR 文件(如 myApp.war)放入 appBase 目录,Tomcat 会根据 unpackWARs 配置(默认 true)自动解压并部署。
处理逻辑:
- 若
unpackWARs="true":解压 WAR 到同名目录,以目录形式部署。 - 若
unpackWARs="false":直接从 WAR 包读取资源(性能较差)。
2. PERIODIC_EVENT:定时扫描更新
HostConfig 定期(默认 10 秒)扫描部署目录,检测以下变化并触发相应操作:
- 新增目录 / WAR 包:自动部署。
- 目录 / WAR 包删除:卸载对应应用。
- 配置文件(如
web.xml、context.xml)修改:触发应用重新加载(需reloadable="true")。
StandardContext:Web 应用的生命周期管理
StandardContext 代表一个 Web 应用,负责初始化资源、启动组件及管理生命周期。其启动过程(startInternal() 方法)包含以下关键步骤:
1. 初始化基础资源
- JNDI 资源:启动应用内配置的数据源、邮件会话等 JNDI 资源。
- Web 资源根:整合应用资源(
WEB-INF/classes、WEB-INF/lib、外部配置的资源等),形成统一的资源访问入口。 - 类加载器:创建
WebappLoader,负责加载应用的类和资源(隔离不同应用的类加载)。 - 临时目录:初始化应用临时文件夹(默认
work/Catalina/localhost/<appName>),用于存储上传文件、JSP 编译结果等。
2. 触发配置事件
发布 CONFIGURE_START_EVENT 事件,由 ContextConfig 监听并完成:
- 解析
web.xml及注解,创建 Servlet、Filter、Listener 等。 - 合并默认配置与应用配置。
3. 启动核心组件
- 子容器(Wrapper):启动所有 Servlet 对应的 StandardWrapper。
- 管道(Pipeline):启动应用级别的阀门(如访问控制、日志记录)。
- 会话管理器:初始化
SessionManager,管理 HTTP 会话(支持内存、数据库、Redis 等存储方式)。 - 监听器与过滤器:实例化并启动
ServletContextListener、Filter等。 - Servlet 初始化:对
loadOnStartup >= 0的 Servlet(如index.jsp对应的 JspServlet),调用init()方法初始化。
4. 状态管理
- 启动成功:设置状态为
STARTED,发布 JMX 通知(可通过监控工具感知)。 - 启动失败:设置状态为
FAILED,记录错误日志。
ContextConfig:解析配置与初始化 Web 容器
ContextConfig 作为 StandardContext 的监听器,通过处理 AFTER_INIT_EVENT、BEFORE_START_EVENT、CONFIGURE_START_EVENT 三个事件,完成应用配置解析和容器初始化。
1. AFTER_INIT_EVENT:合并配置
在 Context 初始化阶段,合并多层次配置(优先级从高到低):
- 应用指定的
configFile(通过context.xml或部署文件设置)。 - Host 级默认配置(
conf/<Engine>/<Host>/context.xml.default)。 - 全局默认配置(
conf/context.xml)。
作用:确保应用配置的灵活性,同时提供统一的默认行为。
2. BEFORE_START_EVENT:修复 docBase
处理 WAR 包部署的路径问题:
- 若
docBase指向 WAR 包且需解压(unpackWARs="true"),自动解压到同名目录,并更新docBase为解压目录。 - 若目录不存在但同名 WAR 包存在,解压 WAR 包作为应用目录。
3. CONFIGURE_START_EVENT:初始化 Web 容器
这是 ContextConfig 最核心的事件,负责解析配置并创建 Web 组件:
(1)解析配置文件
- 默认配置:加载 Tomcat 内置的
conf/web.xml(定义默认 Servlet、MIME 类型等)。 - 应用配置:解析应用的
WEB-INF/web.xml。 - 片段配置:扫描 JAR 包中的
META-INF/web-fragment.xml(Servlet 3.0+ 特性,支持模块化配置),并按优先级合并。
(2)处理注解
当 ignoreAnnotations="false"(默认)时,扫描应用类中的注解:
- Servlet 相关:
@WebServlet、@WebFilter、@WebListener等。 - JNDI 资源:
@Resource等,自动注入依赖。
(3)初始化组件
- Servlet:为每个 Servlet 创建
StandardWrapper,设置loadOnStartup优先级。 - Filter:实例化
Filter并注册到过滤链。 - 安全配置:解析
<security-constraint>、<login-config>等,初始化认证器(Authenticator)。
StandardWrapper:Servlet 的生命周期管理
StandardWrapper 是 Servlet 的容器,负责单个 Servlet 的加载、初始化、调用和销毁。
1. 启动阶段(start ())
- 初始化
ServletConfig,封装 Servlet 配置参数。 - 对
loadOnStartup >= 0的 Servlet,调用load()方法加载。
2. Servlet 加载(load ())
- 实例化:通过
InstanceManager创建 Servlet 实例(支持依赖注入)。 - 注解处理:解析
@MultipartConfig(文件上传配置)、@ServletSecurity(安全约束)等注解。 - 初始化:调用
Servlet.init(ServletConfig)方法,完成 Servlet 初始化。
3. 请求处理
当请求匹配 Servlet 映射时,StandardWrapper 通过 allocate() 方法获取 Servlet 实例(单例模式,除非设置 load-on-startup 为负数且 singleThreadModel 为 true),并调用 service() 方法处理请求。
Context 命名规则
Context 的 name、path、version 与部署文件名相关,遵循以下规则:
- 无版本号:
path="/foo/bar"→ 基础文件名为foo#bar.xml(/替换为#)。path="/"→ 基础文件名为ROOT.xml。
- 有版本号如version=”2”):
path="/foo/bar"→ 基础文件名为foo#bar##2.xml(版本号前加##)。name为path + "##" + version(如/foo/bar##2)。
作用:支持多版本应用并行部署(Servlet 3.0+ 特性)。