因此Catalina在JVM中注册一个 关闭钩子。
public void start() {
// 1. 如果持有的Server实例为空,就解析server.xml创建出来
if (getServer() == null) {
load();
}
// 2. 如果创建失败,报错退出
if (getServer() == null) {
log.fatal(sm.getString(“catalina.noServer”));
return;
}
// 3.启动Server
try {
getServer().start();
} catch (LifecycleException e) {
return;
}
// 创建并注册关闭钩子
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
// 监听停止请求
if (await) {
await();
stop();
}
}
[](()关闭钩子
若需在JVM关闭时做一些清理,比如:
-
将缓存数据刷盘
-
清理一些临时文件
就可以向JVM注册一个关闭钩子,其实就是个线程,JVM在停止之前会尝试执行该线程的run()。
Tomcat的关闭钩子 就是CatalinaShutdownHook:
Tomcat的“关闭钩子”实际上就执行了Server#stop,会释放和清理所有资源。
[](()Server组件
=======================================================================
Server组件具体实现类StandardServer。
Server继承了LifecycleBase,它的生命周期被统一管理
它的子组件是Service,因此它还需要管理Service的生命周期,即在启动时调用Service组件的启动方法,在停止时调用它们的停止方法。Server在内部维护了若干Service组件,它是以数组来保存的,那Server是如何添加一个Service到数组中的呢?
@Override
public void addService(Service service) {
service.setServer(this);
synchronized (servicesLock) {
// 长度+1的数组并没有一开始就分配一个很长的数组
// 而是在添加的过程中动态地扩展数组长度,当添加一个新的Service实例时
// 会创建一个新数组并把原来数组内容复制到新数组,节省内存
Service results[] = new Service[services.length + 1];
// 复制老数据
System.arraycopy(services, 0, results, 0, services.length);
results[services.length] = service;
services = results;
// 启动Service组件
if (getState().isAvailable()) {
try {
service.start();
} catch (LifecycleException e) {
// Ignore
}
}
// 触发监听事件
support.firePropertyChange(“service”, null, service);
}
}
Server组件还需要启动一个Socket来监听停止端口,所以才能通过shutdown命令关闭Tomcat。
上面Catalina的启动方法最后一行代码就是调用Server#await。
在await方法里会创建一个Socket监听8005端口,并在一个死循环里接收Socket上的连接请求,如果有新的连接到来就建立连接,然后从Socket中读取数据;如果读到的数据是停止命令“SHUTDOWN”,就退出循环,进入stop流程。
[](()Service组件
========================================================================
Service组件的具体实现类StandardService
public class StandardService extends LifecycleBase implements Service {
//名字
private String name = null;
//Server实例
private Server server = null;
//连接器数组
protected Connector connectors[] = new Connector[0];
private final Object connectorsLock = new Object();
//对应的Engine容器
private Engine engine = null;
//映射器及其监听器
protected final Mapper mapper = new Mapper();
protected final MapperListener mapperListener = new MapperListener(this);
StandardService继承了LifecycleBase抽象类,此外StandardService中还有一些我们熟悉的组件,比如Server、Connector、Engine和Mapper。
Tomcat支持热部署,当Web应用的部署发生变化,Mapper中的映射信息也要跟着变化,MapperListener就是监听器,监听容器的变化,并把信息更新到Mapper。
[](()Service启动方法
protected void startInternal() throws LifecycleException {
// 1. 触发启动监听器
setState(LifecycleState.STARTING);
// 2. 先启动Engine,Engine会启动它子容器
if (engine != null) {
synchronized (engine) {
engine.start();
}
}
// 3. 再启动Mapper监听器
mapperListener.start();
// 4.最后启动连接器,连 《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 接器会启动它子组件,比如Endpoint
synchronized (connectorsLock) {
for (Connector connector: connectors) {
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
}
}
}