0%

tomcat之web应用加载

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
2
3
4
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<!-- docBase:Web应用根目录路径;path:访问路径 -->
<Context docBase="myApp" path="/myApp" reloadable="true"/>
</Host>

特点

  • 需手动修改 server.xml,不适合动态部署。
  • reloadable="true" 表示当应用类或配置文件变化时,自动重新加载。

2. StandardHost 启动加载流程

StandardHost 的启动过程(startInternal() 方法)是 Web 应用加载的触发点,关键步骤包括:

  1. 添加错误处理阀门:注册 ErrorReportValve,用于生成默认错误页面(当应用未配置错误页时)。
  2. 启动子组件
    • 启动集群组件(Cluster)和安全组件(Realm)。
    • 启动 server.xml 中配置的 <Context> 实例(静态加载的应用)。
  3. 触发部署事件:设置 Host 状态为 STARTING,触发 START_EVENT 事件,由 HostConfig 监听并执行自动部署。
  4. 启动后台任务:如集群同步、安全认证等周期性任务。

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
2
3
<Context docBase="/path/to/myApp" path="/myApp" reloadable="false">
<WatchResource>WEB-INF/web.xml</WatchResource> <!-- 监听配置变化 -->
</Context>
  • docBase:应用实际目录或 WAR 包路径。
  • path:访问路径(为空时默认为文件名)。
(2)Web 目录部署

将 Web 应用目录(包含 WEB-INFMETA-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.xmlcontext.xml)修改:触发应用重新加载(需 reloadable="true")。

StandardContext:Web 应用的生命周期管理

StandardContext 代表一个 Web 应用,负责初始化资源、启动组件及管理生命周期。其启动过程(startInternal() 方法)包含以下关键步骤:

1. 初始化基础资源

  • JNDI 资源:启动应用内配置的数据源、邮件会话等 JNDI 资源。
  • Web 资源根:整合应用资源(WEB-INF/classesWEB-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 等存储方式)。
  • 监听器与过滤器:实例化并启动 ServletContextListenerFilter 等。
  • Servlet 初始化:对 loadOnStartup >= 0 的 Servlet(如 index.jsp 对应的 JspServlet),调用 init() 方法初始化。

4. 状态管理

  • 启动成功:设置状态为 STARTED,发布 JMX 通知(可通过监控工具感知)。
  • 启动失败:设置状态为 FAILED,记录错误日志。

ContextConfig:解析配置与初始化 Web 容器

ContextConfig 作为 StandardContext 的监听器,通过处理 AFTER_INIT_EVENTBEFORE_START_EVENTCONFIGURE_START_EVENT 三个事件,完成应用配置解析和容器初始化。

1. AFTER_INIT_EVENT:合并配置

在 Context 初始化阶段,合并多层次配置(优先级从高到低):

  1. 应用指定的 configFile(通过 context.xml 或部署文件设置)。
  2. Host 级默认配置(conf/<Engine>/<Host>/context.xml.default)。
  3. 全局默认配置(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 为负数且 singleThreadModeltrue),并调用 service() 方法处理请求。

Context 命名规则

Context 的 namepathversion 与部署文件名相关,遵循以下规则:

  • 无版本号:
    • path="/foo/bar" → 基础文件名为 foo#bar.xml/ 替换为 #)。
    • path="/" → 基础文件名为 ROOT.xml
  • 有版本号如version=”2”):
    • path="/foo/bar" → 基础文件名为 foo#bar##2.xml(版本号前加 ##)。
    • namepath + "##" + version(如 /foo/bar##2)。

作用:支持多版本应用并行部署(Servlet 3.0+ 特性)。

欢迎关注我的其它发布渠道