Tomcat 生命周期与容器架构源码分析
概述
Tomcat 中几乎每个核心组件——Server、Service、Connector、Engine、Host、Context、Wrapper——都实现 Lifecycle 接口。这套统一的生命周期框架让组件树能够按确定性顺序启动与停止,也保证了资源管理的可预期。本文深入 Lifecycle 接口、LifecycleBase 模板实现、事件机制与容器递归启停的源码细节。
一、Lifecycle 接口与状态机
1.1 接口定义
// org.apache.catalina.Lifecycle
public interface Lifecycle {
void init(); // 初始化:创建资源,不对外服务
void start(); // 启动:开始提供服务
void stop(); // 停止:停止服务,可再次 start
void destroy(); // 销毁:释放所有资源,不可恢复
// 生命周期事件监听
void addLifecycleListener(LifecycleListener listener);
void removeLifecycleListener(LifecycleListener listener);
LifecycleListener[] findLifecycleListeners();
}1.2 状态枚举
// org.apache.catalina.LifecycleState —— 状态机
public enum LifecycleState {
NEW(false, null),
INITIALIZING(false, Lifecycle.BEFORE_INIT_EVENT),
INITIALIZED(false, Lifecycle.AFTER_INIT_EVENT),
STARTING_PREP(false, Lifecycle.BEFORE_START_EVENT),
STARTING(true, Lifecycle.START_EVENT),
STARTED(true, Lifecycle.AFTER_START_EVENT),
STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT),
STOPPING(false, Lifecycle.STOP_EVENT),
STOPPED(false, Lifecycle.AFTER_STOP_EVENT),
DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT),
DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT),
FAILED(false, null);
private final boolean available; // 是否处于"可用"状态(可服务请求)
private final String lifecycleEvent; // 关联的生命周期事件名
}状态机要点:
- available 标志:只有
STARTING/STARTED为 true,其余状态均不可服务请求 - 双向状态:每个状态都有
_PREP与事件名关联,供监听器感知阶段变化 - FAILED 兜底:任何阶段异常都会进入
FAILED,保证状态不悬空
二、LifecycleBase:模板方法模式
LifecycleBase 实现接口的公共逻辑,子类只需覆写 initInternal() 等钩子方法:
// org.apache.catalina.util.LifecycleBase —— 简化
public abstract class LifecycleBase implements Lifecycle {
private LifecycleState state = LifecycleState.NEW;
@Override
public final void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
// 非法状态校验:只有 NEW 才能 init
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
initInternal(); // ← 子类实现
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "init");
}
}
@Override
public final void start() throws LifecycleException {
// 状态分派:NEW → init 后 start;STARTED 直接返回;其余状态校验
if (LifecycleState.STARTING_PREP.equals(state) ||
LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {
return; // 幂等:已启动直接返回
}
...
setStateInternal(LifecycleState.STARTING_PREP, null, false);
startInternal(); // ← 子类实现
setStateInternal(LifecycleState.STARTED, null, false);
}
protected abstract void initInternal() throws LifecycleException;
protected abstract void startInternal() throws LifecycleException;
protected abstract void stopInternal() throws LifecycleException;
protected abstract void destroyInternal() throws LifecycleException;
}设计价值:
- 状态校验集中:非法转换统一拦截,子类不感知
- 事件自动触发:
setStateInternal在状态变化时同步派发生命周期事件 - 钩子最小化:子类只实现 4 个
*Internal()方法 - 幂等保护:重复 start/stop 不产生副作用
三、事件机制:LifecycleEvent 与监听器
3.1 事件触发
setStateInternal 内部创建事件并通知监听器:
// LifecycleBase.setStateInternal() 简化
private void setStateInternal(LifecycleState state, Object data, boolean check) {
...
this.state = state;
// 状态关联的事件名(如 AFTER_START_EVENT = "after_start")
String lifecycleEvent = state.getLifecycleEvent();
if (lifecycleEvent != null) {
fireLifecycleEvent(lifecycleEvent, data); // 派发事件
}
}
// 通知所有监听器
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);
}
}3.2 典型监听器
| 监听器 | 监听组件 | 作用 |
|---|---|---|
HostConfig | Host | 部署/卸载 Context(应用热部署的核心) |
ContextConfig | Context | 解析 web.xml、初始化 Servlet 上下文 |
EngineConfig | Engine | 引擎级事件记录 |
AprLifecycleListener | Server | APR native 库加载 |
JreMemoryLeakPreventionListener | Server | 防 JVM 级内存泄漏 |
以 ContextConfig 为例,它在 Context 的 CONFIGURE_START_EVENT 时触发 web.xml 解析:
// ContextConfig.lifecycleEvent()
public void lifecycleEvent(LifecycleEvent event) {
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
configureStart(); // 解析默认 web.xml + 应用 web.xml
}
}事件驱动解耦:容器自身不直接调用配置逻辑,而是通过事件通知——这正是模板方法 + 观察者模式的组合。
四、容器递归启停
4.1 启动的递归
容器组件(Engine/Host/Context)继承 ContainerBase,它实现了 startInternal() 的递归逻辑:
// org.apache.catalina.core.ContainerBase.startInternal() 简化
protected synchronized void startInternal() throws LifecycleException {
// 1. 启动子容器(递归)
Container children[] = findChildren();
for (Container child : children) {
if (child.getState().isAvailable()) continue; // 已启动跳过
child.start(); // 递归启动子容器
}
// 2. 启动自己的 Pipeline(Valve 链)
pipeline.start();
// 3. 启动后台线程(backgroundProcessor:Session 扫描、热部署检查等)
threadStart();
}顺序保证:子容器先启动,再启动自身管道与后台线程——叶子(Wrapper 的 Servlet)最后就绪。
4.2 停止的递归(逆序)
// stopInternal() 的停止顺序与启动相反
protected synchronized void stopInternal() throws LifecycleException {
// 1. 先停自身后台线程
threadStop();
// 2. 停 Pipeline
pipeline.stop();
// 3. 逆序停止子容器
Container children[] = findChildren();
for (int i = children.length - 1; i >= 0; i--) {
children[i].stop();
}
}先停后台任务,再停管道,最后逆序停子容器——保证不再产生新任务后逐步释放资源。
4.3 Connector 的启停细节
// Connector.startInternal() 关键逻辑
protected void startInternal() throws LifecycleException {
// 协议处理器初始化并开始监听
protocolHandler.start();
}停止时先停止接收新连接,再处理存量请求(drain),这是优雅停机的关键。
五、后台线程:backgroundProcessor
每个容器可以启动后台处理线程,周期性执行维护任务:
| 容器 | 后台任务 |
|---|---|
| Host | 热部署扫描(autoDeploy) |
| Context | Session 过期扫描、JSP 更新检查 |
| Engine | 会话复制心跳等 |
// ContainerBase.threadStart() —— 启动后台线程
protected void threadStart() {
if (backgroundProcessorDelay > 0 && !backgroundThreadStarted) {
...
backgroundThread = new Thread(new ContainerBackgroundProcessor(), ...);
backgroundThread.start();
}
}backgroundProcessorDelay(默认 10 秒)控制扫描周期——这是热部署"延迟生效"的原因。
六、生命周期事件在启动中的完整序列
以 Context 为例,启动时依次触发:
init()
INITIALIZING → initInternal() → INITIALIZED
↓
start()
STARTING_PREP
→ 触发 BEFORE_START_EVENT(ContextConfig 开始配置)
startInternal()
→ 创建类加载器、解析 web.xml、初始化监听器
→ 触发 CONFIGURE_START_EVENT(配置完成)
→ 触发 START_EVENT(启动 Servlet 上下文)
STARTED
→ 触发 AFTER_START_EVENT每一阶段都有对应事件,监听器(如监控系统)可以挂钩任意环节统计耗时。
七、自定义组件的生命周期接入
想在 Tomcat 中扩展组件并纳入生命周期管理,只需继承 LifecycleMBeanBase 并实现钩子:
// 自定义 Valve 示例(生命周期 + 管道组件)
public class MyValve extends ValveBase {
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
long start = System.currentTimeMillis();
getNext().invoke(request, response);
long cost = System.currentTimeMillis() - start;
// 记录耗时日志
}
}配置到 server.xml:
<Host name="localhost" appBase="webapps">
<Valve className="com.example.MyValve" />
</Host>容器启动时 Valve 随 Pipeline 自动 init/start,纳入同一生命周期管理。
八、总结
| 设计 | 机制 | 价值 |
|---|---|---|
| 状态机 | LifecycleState 枚举 + 状态校验 | 非法转换被拦截,状态可观测 |
| 模板方法 | LifecycleBase + *Internal() 钩子 | 公共逻辑统一,子类最小化 |
| 观察者模式 | 生命周期事件 + 监听器 | 启动/停止逻辑解耦,便于扩展 |
| 递归启停 | ContainerBase 级联子容器 | 组件树顺序确定性,优雅停机 |
Tomcat 的生命周期框架是所有组件协作的"总调度协议"——读懂了它,启动日志、热部署、优雅停机、内存泄漏等一系列现象都能串起来理解。
参考链接: