Tomcat 的启动过程?
我们来详细解析一下 Tomcat 的启动过程。这是一个从执行脚本到各个组件初始化并对外提供服务的完整流程。
理解这个过程对于深入掌握 Tomcat 架构、进行性能调优和故障排查至关重要。
核心思想:分层与模块化
Tomcat 的设计遵循了分层和模块化的思想。其启动过程可以看作是一个“自顶向下”的初始化链条:
- 启动脚本 -> 2. Catalina 容器 -> 3. Server 服务 -> 4. Service 服务 -> 5. Engine/Host/Context/Warpper。
启动过程详细步骤
整个启动流程可以概括为下图所示的层级结构:
plaintext
flowchart TD
A[执行启动脚本<br>startup.bat/sh] --> B[调用Bootstrap.main]
B --> C[Bootstrap.init<br>初始化类加载器]
C --> D[Bootstrap.load<br>创建并配置Catalina实例]
D --> E[Bootstrap.start<br>启动Catalina]
subgraph S [Catalina 生命周期管理]
direction TB
F[Catalina.start] --> G[解析server.xml<br>创建Server实例]
G --> H[Server.init<br>初始化所有Service]
H --> I[Server.start<br>启动所有Service]
subgraph T [Service 组件工作流]
direction LR
J[Container<br>Engine] --> K[Host] --> L[Context] --> M[Wrapper]
end
I -- 触发 --> J
end
E --> F
下面我们来详细拆解图中的每一步。
第1步:执行启动脚本 (startup.bat / startup.sh)
- 这是用户操作的入口。在 Windows 上是
startup.bat,在 Linux/macOS 上是startup.sh。 - 这些脚本的核心作用是设置好 Java 运行环境(如
JAVA_HOME,CATALINA_HOME等环境变量),然后最终调用org.apache.catalina.startup.Bootstrap类的main方法。
第2步:初始化 Bootstrap 类
Bootstrap.main()方法是 Java 程序的真正起点。- 初始化类加载器:首先会初始化三个重要的类加载器,它们构成了 Tomcat 的类加载体系,实现了“父类委托机制”的破坏,使得 Web 应用可以优先加载自己的库。
Common ClassLoader: 加载$CATALINA_HOME/lib下的 jar 包,被所有组件共享。Catalina ClassLoader: 加载$CATALINA_HOME/server下的类,用于隔离 Tomcat 自身代码。Shared ClassLoader: 加载$CATALINA_BASE/shared下的 jar 包,可被所有 Web 应用共享(默认不启用)。
- 反射创建 Catalina 实例:通过反射机制实例化
org.apache.catalina.startup.Catalina对象,并将自己(Bootstrap)设置为 Catalina 的父加载器。
第3步:解析配置并启动 Catalina
- Bootstrap 调用 Catalina 的
load()和start()方法。 load()方法:- 解析
conf/server.xml配置文件(也可以解析指定的其他文件)。这个文件定义了 Tomcat 的核心组件层次结构:Server->Service->Connector&Engine。 - 根据解析结果,使用 Digester 库创建出对应的 Java 对象(
StandardServer,StandardService,Connector,StandardEngine等),并建立它们之间的关联关系。
- 解析
start()方法:- 调用
Server组件的start()方法,正式开启启动流程。
- 调用
第4步:启动 Server 服务
Server是 Tomcat 的最顶层组件,代表整个 Servlet 容器。它的默认实现是StandardServer。Server.start()方法会触发其生命中期监听器(Lifecycle Listeners)的lifecycleEvent事件,其中最重要的是 NamingResources 的初始化(用于 JNDI 资源)。- 然后,它会遍历自己持有的所有
Service组件,并依次调用它们的start()方法。一个 Tomcat 实例通常只有一个Server,但可以配置多个Service(例如,将连接器分组)。
第5步:启动 Service 服务
Service组件(默认实现StandardService)是Connector(连接器)和Container(容器)的桥梁。它负责管理一个或多个连接器和一个引擎。Service.start()方法主要做两件事:- 启动 Engine(容器):调用
Engine.start()。 - 启动所有 Connector(连接器):遍历所有连接器(如 HTTP/1.1, AJP/1.3 等),并调用它们的
start()方法。
- 启动 Engine(容器):调用
第6步:启动 Connector(连接器)
- 连接器负责接收客户端请求(如浏览器),并交给容器处理,最后返回响应。
Connector.start()的核心是启动其内部的一个或多个 ProtocolHandler。- 例如,
Http11NioProtocol用于处理 HTTP/1.1 协议并使用 NIO 模式。
- 例如,
- ProtocolHandler 会启动一个 Endpoint(如
NioEndpoint)来监听网络端口(如 8080)。 - Endpoint 会初始化并启动 Acceptor 线程(用于接收新连接)和 Poller 线程(用于监控已建立连接的读写事件),以及一个 Executor 线程池(用于执行具体的请求处理任务)。此时,Tomcat 已经可以接受外部请求了。
第7步:启动 Container(容器)—— 核心中的核心
这是最复杂的部分,涉及容器的嵌套结构:Engine -> Host -> Context -> Wrapper。
- 启动 Engine:
Engine(默认实现StandardEngine)是顶级容器,代表一个 Servlet 引擎。它的start()方法会设置其pipeline(管道)和valve(阀门),然后启动其唯一的子容器——Host。
- 启动 Host:
Host(默认实现StandardHost)代表一个虚拟主机(如localhost)。它的start()方法同样会设置管道和阀门,然后遍历其所有子容器——Context,并依次启动它们。
- 启动 Context:
Context(默认实现StandardContext)代表一个 Web 应用程序(即一个 WAR 包或webapps/下的一个目录)。这是启动过程中非常关键的一步。- 它的
start()方法会按顺序完成以下重要工作:- 创建 WebappClassLoader:为每个 Web 应用创建独立的类加载器,实现应用间的隔离。
- 解析
web.xml:合并全局web.xml、web-fragment.xml和应用自身的web.xml,构建 Servlet、Filter、Listener 的定义。 - 初始化 Listener:按照在
web.xml中声明的顺序,实例化并调用所有ServletContextListener的contextInitialized方法。Spring 的ContextLoaderListener就是在这里被调用的,从而启动了 Spring 容器。 - 初始化 Filter。
- 加载并初始化 Servlet:对于在
web.xml中使用<load-on-startup>标签配置的 Servlet(如 Spring MVC 的DispatcherServlet),会在此刻被实例化并调用其init()方法。
- 启动 Wrapper:
Wrapper是最小的容器,代表一个具体的 Servlet(如DispatcherServlet)。它的start()方法主要是确保关联的 Servlet 已被正确加载和初始化。
至此,Tomcat 的启动过程全部完成。服务器已经处于运行状态,可以接收和处理来自客户端的 HTTP 请求。
总结与要点
| 阶段 | 核心动作 | 关键组件 |
|---|---|---|
| 1. 脚本调用 | 设置环境,调用 Bootstrap.main() |
startup.bat/sh |
| 2. 引导层 | 初始化类加载器,创建 Catalina | Bootstrap |
| 3. 配置解析 | 解析 server.xml,构建组件树 |
Catalina, Digester |
| 4. 服务层 | 启动 Server 和其下的 Services | StandardServer, StandardService |
| 5. 连接层 | 启动 Connector,监听端口 | Connector, ProtocolHandler, Endpoint |
| 6. 容器层 | 递归启动 Engine -> Host -> Context -> Wrapper | StandardEngine, StandardHost, StandardContext, StandardWrapper |
| 7. 应用初始化 | 加载 Web 应用,启动 Spring 等框架 | WebappClassLoader, ServletContextListener, Filter, Servlet |
关键理解点:
- 生命周期统一管理:Tomcat 的所有核心组件都实现了
Lifecycle接口,通过一个状态机(INITIALIZED -> STARTING_PREP -> STARTING -> STARTED ...)来管理生命周期,保证了启动和停止过程的顺序性和一致性。 - 管道-阀门模型:每个容器都有一个 Pipeline(管道)和多个 Valve(阀门)。请求在容器内传递的过程就是依次经过这些阀门的过程,这是一种责任链模式的优雅实现。
- 类加载器隔离:Tomcat 打破了 JVM 的双亲委派模型,设计了多级类加载器,确保了 Web 应用的独立性和安全性。
- 与 Spring 的集成点:Spring 容器的启动是由
ContextLoaderListener(一个 ServletContextListener)触发的,而这个 Listener 的初始化发生在StandardContext.start()阶段。因此,Spring 容器的生命周期是内嵌在 Tomcat 的 Context 组件生命周期之中的。
右滑查看面试常问