一切的 Java 程序入口都是 main 方法, Tomcat 的启动类是哪个呢?
直接启动 Tomcat, 使用 jps -l
命令 (Linux 也可以使用 ps -ef | grep java
), 查看本机上所有正在运行的 Java 程序
可以看到, Tomcat 的主启动类是 org.apache.catalina.startup.Bootstrap
初始化各个组件
先来到 Bootstrap 类, 查看 Bootstrap 的 main 方法
synchronized (daemonLock) {
if (daemon == null) {
Bootstrap bootstrap = new Bootstrap();
try {
// 初始化类加载器, 并创建 Catalina 对象
bootstrap.init();
}
....
daemon = bootstrap;
}
else {
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
}
try {
String command = "start";
....
// 默认进入此行代码
else if (command.equals("start")) {
daemon.setAwait(true);
// 利用反射调用 Catalina 的 load 方法, 加载各个组件
daemon.load(args);
// 启动各个组件
daemon.start();
if (null == daemon.getServer()) {
System.exit(1);
}
}......
}
Catalina
查看 load 方法
// 创建一个解析 xml 文件的对象
Digester digester = createStartDigester();
try {
try {
// 拿到 server.xml 配置文件
file = configFile();
.....
}
try {
...
// 完成对 server.xml 配置文件的解析
// 并创建了默认的组件对象 : StandardServer, StandardService
// StandardEngine, StandardHost, Connector, Executor, Mapper
// 并为 Server 添加了六个监听器, 为 Engine 添加监听器 : EngineConfig
// 为 Host 添加监听器 : HostConfig
digester.parse(inputSource);
}
try {
// 初始化 Server 组件
getServer().init();
}
}
接下来初始化各个组件, 其中 StandardServer, StandardService, StandardEngine, Connector, 都继承自 LifecycleBase
抽象类, 这里使用了 模板设计模式
, 将公共代码抽取出来放到他们的公共父类中
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
// 标记组件生命周期的状态
setStateInternal(LifecycleState.INITIALIZING, null, false);
// 初始化各个组件 Server -> Service -> Engine -> MapperListener -> Connector
// 在启动 Container 组件的时候又会初始化 Host, Context, Wrapper, Pipeline, Valve 组件
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
}
....
}
监听器
Server 默认存在六个生命周期监听器 (这些监听器对应 server.xml 配置文件中配置的监听器), 监听 Server 在生命周期中发生的事件, 并进行相应处理
-
NameContextListener
用于初始化和填充与每个 Context 和 Server 关联的的 JNDI 服务
-
VersionLoggerListener
用于以日志形式输出服务器 、操作系统、JVM的版本信息
-
AprLifecycleListener
用于加载 (服务器启动) 和 销毁 (服务器停止) APR。 如果找不到 APR 库, 则会输出日志, 并不影响Tomcat启动
-
JreMemoryLeakPreventionListener
用于避免 JRE 内存泄漏问题
-
GlobalResourcesLifecycleListener
用户加载 (服务器启动) 和 销毁 (服务器停止) 全局命名服务
-
ThreadLocalLeakPreventionListener
用于在 Context 停止时重建 Executor 池中的线程, 以避免 ThreadLocal 相关的内存泄漏
Lifecycle
由于所有的组件均存在初始化、启动、停止等生命周期方法,拥有生命周期管理的特性, 所以 Tomcat 在设计的时候, 基于生命周期管理抽象成了一个接口 Lifecycle ,而组件 Server、Service、Container、Executor、Connector 组件 , 都实现了 Lifecycle 接口,从而具有了以下生命周期中的核心方法:
- init() :初始化组件
- start() :启动组件
- stop() :停止组件
- destroy() :销毁组件
在 Lifecycle 的 Javadoc 文档中, 有这样的一段注释
描述了各个组件执行完对应的方法后, 应处于哪种生命周期状态
各组件的默认实现
上面提到的 Server、Service、Engine、Host、Context 都是接口, 的默认实现类都是加上 Standard
前缀的, 比如 StandardServer, StandardService …
ProtocolHandler : Coyote 协议接口,通过封装 Endpoint 和 Processor , 实现针对具体协议的处理功能。Tomcat按照协议和 IO 提供了6个实现类。
AJP协议:
- AjpNioProtocol :采用 NIO 的 IO 模型。
- AjpNio2Protocol:采用 NIO2 的IO模型。
- AjpAprProtocol :采用 APR 的 IO 模型,需要依赖于 APR 库。
HTTP协议:
- Http11NioProtocol :采用 NIO 的 IO 模型,默认使用的协议(如果服务器没有安装APR)。
- Http11Nio2