深入剖析Tomcat(六) Tomcat各组件的生命周期控制

Catalina中有很多组件,像上一章提到的四种容器,载入器,映射器等都是一种组件。每个组件在对外提供服务之前都需要有个启动过程;组件在销毁之前,也需要有个关闭过程;例如servlet容器关闭时,需要调用servlet的destroy方法,session管理器关闭时必须将seesion对象保存到辅助存储器中等。

Catalina为了更好的控制各组件的启动与销毁流程,做了几个生命周期的接口与工具类出来,对所有需要生命周期控制的组件做了个规范,并将启动与销毁的触发点收束到一个顶层组件中,实现统一启动或关闭所有组件。

这个统一控制的实现方式就是,由最顶层组件发号施令,一层一层往下传播,每个组件只控制其子组件的启动与关闭,直到最底层的组件。

相关的接口与类有以下几个,下面一一介绍

org.apache.catalina.Lifecycle

org.apache.catalina.LifecycleEvent

org.apache.catalina.LifecycleListener

org.apache.catalina.util.LifecycleSupport

Lifecycle接口

Catalina中的组件如果想被纳入统一生命周期管理的话,就必须实现Lifecycle接口。

该接口定义了五个方法与六个字符串属性。

最重要的方法是start()与stop()方法,这两个方法必须被实现。

start()方法中需要执行本组件的启动逻辑并调用子组件的start()方法。stop()方法需要执行本组件的关闭逻辑并调用子组件的stop()方法。Catalina的这种设计使得所有子组件都置于其父组件的“监护”之下,这样,Catalina的启动类只需要启动一个组件就可以将全部应用的组件都启动起来。

六个属性则是定义了start与stop过程中的六个阶段的事件

  • BEFORE_START_EVENT:组件启动前
  • START_EVENT:                 组件启动中
  • AFTER_START_EVENT:   组件启动后
  • BEFORE_STOP_EVENT:  组件关闭前
  • STOP_EVENT:                   组件关闭中
  • AFTER_STOP_EVENT:     组件关闭后

六种事件发生后,需要有专门的事件监听器来处理。Lifecycle接口中其他三个方法则是与事件监听器相关的方法:添加、移除、查询 事件监听器。

public interface Lifecycle {public static final String START_EVENT = "start";public static final String BEFORE_START_EVENT = "before_start";public static final String AFTER_START_EVENT = "after_start";public static final String STOP_EVENT = "stop";public static final String BEFORE_STOP_EVENT = "before_stop";public static final String AFTER_STOP_EVENT = "after_stop";public void addLifecycleListener(LifecycleListener listener);public LifecycleListener[] findLifecycleListeners();public void removeLifecycleListener(LifecycleListener listener);public void start() throws LifecycleException;public void stop() throws LifecycleException;}

LifecycleEvent类

这个类是对Lifecycle中六种事件的封装,组件触发生命周期的事件后,将事件封装成LifecycleEvent对象,然后作为监听器的方法参数去触发监听。其中type字段放的就是Lifecycle中定义的六个事件之一。

public final class LifecycleEvent extends EventObject {public LifecycleEvent(Lifecycle lifecycle, String type) {this(lifecycle, type, null);}public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {super(lifecycle);this.lifecycle = lifecycle;this.type = type;this.data = data;}// 事件关联的数据private Object data = null;// 触发事件的组件private Lifecycle lifecycle = null;// 事件类型private String type = null;public Object getData() {return this.data;}public Lifecycle getLifecycle() {return this.lifecycle;}public String getType() {return (this.type);}}

LifecycleListener接口

这是事件监听器的接口。实现了Lifecycle接口的组件,在触发了一个事件后,将事件封装成LifecycleEvent对象,调用监听器的lifecycleEvent,根据不同事件做相应处理。

package org.apache.catalina;public interface LifecycleListener {/*** 让监听器知晓有事件发生了*/public void lifecycleEvent(LifecycleEvent event);}

LifecycleSupport类

此类是一个工具类,负责辅助 实现了Lifecycle的组件 将LifecycleEvent事件通知到所有监听器。

LifecycleSupport类有两个属性:

  • lifecycle:关联的 实现了Lifecycle接口的组件
  • listeners:lifecycle关联的监听器

addLifecycleListenerremoveLifecycleListener两个方法,通过复制监听器数组的方式来新增或移除监听器。

fireLifecycleEvent方法负责将事件封装成LifecycleEvent对象并作为参数,for循环调用所有监听器的lifecycleEvent方法,执行监听处理。

public final class LifecycleSupport {public LifecycleSupport(Lifecycle lifecycle) {super();this.lifecycle = lifecycle;}private Lifecycle lifecycle = null;private LifecycleListener[] listeners = new LifecycleListener[0];/*** 为lifecycle组件添加一个事件监听器*/public void addLifecycleListener(LifecycleListener listener) {synchronized (listeners) {LifecycleListener results[] = new LifecycleListener[listeners.length + 1];for (int i = 0; i < listeners.length; i++) {results[i] = listeners[i];}results[listeners.length] = listener;listeners = results;}}public LifecycleListener[] findLifecycleListeners() {return listeners;}/*** 通知所有事件监听器,此容器发生了特定事件,默认使用调用线程以同步的方式执行此方法。*/public void fireLifecycleEvent(String type, Object data) {LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);LifecycleListener[] interested;// 这里克隆一下listeners数组,我理解克隆的意义 是为了防止在执行listeners的for循环过程中,又被调用了addLifecycleListener来添加一个listenersynchronized (listeners) {interested = listeners.clone();}for (int i = 0; i < interested.length; i++) {interested[i].lifecycleEvent(event);}}/*** 移除一个事件监听器*/public void removeLifecycleListener(LifecycleListener listener) {synchronized (listeners) {int n = -1;for (int i = 0; i < listeners.length; i++) {if (listeners[i] == listener) {n = i;break;}}if (n < 0) return;LifecycleListener results[] = new LifecycleListener[listeners.length - 1];int j = 0;for (int i = 0; i < listeners.length; i++) {if (i != n) results[j++] = listeners[i];}listeners = results;}}}

其他类

本章其他的类基本沿用了第五章的类。不同点是,本章将各个组件类都实现了Lifecycle接口。

SimpleContext最为最顶层的组件,负责“一键启停”所有组件。SimpleContext拥有一个LifecycleSupport属性,用来处理对生命周期事件的监听。本章做了一个SimpleContextLifecycleListener类来作为SimpleContext的事件监听器。在start()启动方法中,会将一个SimpleContextLifecycleListener实例与SimpleContext绑定起来。

下面是SimpleContext的部分代码

public class SimpleContext implements Context, Pipeline, Lifecycle {public SimpleContext() {pipeline.setBasic(new SimpleContextValve());}// 子容器protected HashMap children = new HashMap();// 载入器private Loader loader = null;// 生命周期支持组件protected LifecycleSupport lifecycle = new LifecycleSupport(this);// 管道private SimplePipeline pipeline = new SimplePipeline(this);// servlet的uri映射private HashMap servletMappings = new HashMap();// 默认映射器protected Mapper mapper = null;// 映射器集合protected HashMap mappers = new HashMap();// 父组件private Container parent = null;// 此组件是否已启动protected boolean started = false;// 添加映射器public void addLifecycleListener(LifecycleListener listener) {lifecycle.addLifecycleListener(listener);}// 移除映射器public void removeLifecycleListener(LifecycleListener listener) {lifecycle.removeLifecycleListener(listener);}public synchronized void start() throws LifecycleException {if (started) {throw new LifecycleException("SimpleContext has already started");}// 触发了特定事件,通知事件监听器lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);started = true;try {// 启动所有从属组件if ((loader != null) && (loader instanceof Lifecycle)) {((Lifecycle) loader).start();}// 启动所有子容器Container children[] = findChildren();for (int i = 0; i < children.length; i++) {if (children[i] instanceof Lifecycle) {((Lifecycle) children[i]).start();}}// 启动管道中的所有阀if (pipeline instanceof Lifecycle) {((Lifecycle) pipeline).start();}// 触发了特定事件,通知事件监听器lifecycle.fireLifecycleEvent(START_EVENT, null);} catch (Exception e) {e.printStackTrace();}// 触发了特定事件,通知事件监听器lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);}public void stop() throws LifecycleException {if (!started) {throw new LifecycleException("SimpleContext has not been started");}// 触发了特定事件,通知事件监听器lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);lifecycle.fireLifecycleEvent(STOP_EVENT, null);started = false;try {// 关闭管道中的所有阀if (pipeline instanceof Lifecycle) {((Lifecycle) pipeline).stop();}// 关闭所有子容器Container children[] = findChildren();for (int i = 0; i < children.length; i++) {if (children[i] instanceof Lifecycle) {((Lifecycle) children[i]).stop();}}if ((loader != null) && (loader instanceof Lifecycle)) {((Lifecycle) loader).stop();}} catch (Exception e) {e.printStackTrace();}// 触发了特定事件,通知事件监听器lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);}}

SimpleContextLifecycleListener监听器

public class SimpleContextLifecycleListener implements LifecycleListener {public void lifecycleEvent(LifecycleEvent event) {Lifecycle lifecycle = event.getLifecycle();System.out.println("SimpleContextLifecycleListener's event " + event.getType());if (Lifecycle.START_EVENT.equals(event.getType())) {System.out.println("Starting context.");} else if (Lifecycle.STOP_EVENT.equals(event.getType())) {System.out.println("Stopping context.");}}}

SimpleLoader(载入器)实现了Lifecycle接口,但是方法都留空了,start与stop方法打印了一下启动信息。虽然这里start方法没有干事,但是它的启动流程已经能交给上层组件来控制了,后面再丰富启动流程也ok。

public synchronized void start() throws LifecycleException {System.out.println("Starting SimpleLoader");
}public void stop() throws LifecycleException {System.out.println("Stopping SimpleLoader");
}

SimplePipeline实现了Lifecycle接口,但是相关方法也留空了。start与stop方法仅仅打印一条信息。

public synchronized void start() throws LifecycleException {System.out.println("Starting SimplePipeline");
}public void stop() throws LifecycleException {System.out.println("Stopping SimplePipeline");
}

SimpleWrapper实现了Lifecycle接口,它是个容器类,包含了多种组件,所以它的start(),stop()方法做了实现。由于SimpleWrapper的每个实例都对应了一个servlet实例,所以在stop方法中,会去调用servlet的destroy方法。

public class SimpleWrapper implements Wrapper, Pipeline, Lifecycle {public SimpleWrapper() {pipeline.setBasic(new SimpleWrapperValve());}// the servlet instanceprivate Servlet instance = null;private String servletClass;private Loader loader;private String name;private SimplePipeline pipeline = new SimplePipeline(this);protected Container parent = null;protected LifecycleSupport lifecycle = new LifecycleSupport(this);protected boolean started = false;// 本章中的SimpleWrapper没有监听器,监听器相关的三个方法留空public void addLifecycleListener(LifecycleListener listener) {}public LifecycleListener[] findLifecycleListeners() {return null;}public void removeLifecycleListener(LifecycleListener listener) {}public synchronized void start() throws LifecycleException {System.out.println("Starting Wrapper " + name);if (started) {throw new LifecycleException("Wrapper already started");}// 触发了特定事件,通知事件监听器lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);started = true;// 启动所有从属组件if ((loader != null) && (loader instanceof Lifecycle)) {((Lifecycle) loader).start();}// 启动管道中的所有阀if (pipeline instanceof Lifecycle) {((Lifecycle) pipeline).start();}// 触发了特定事件,通知事件监听器lifecycle.fireLifecycleEvent(START_EVENT, null);// 触发了特定事件,通知事件监听器lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);}public void stop() throws LifecycleException {System.out.println("Stopping wrapper " + name);// 销毁servlet实例 (如果它已经被初始化的话)try {instance.destroy();} catch (Throwable t) {}instance = null;if (!started) {throw new LifecycleException("Wrapper " + name + " not started");}// 触发了特定事件,通知事件监听器lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);// 触发了特定事件,通知事件监听器lifecycle.fireLifecycleEvent(STOP_EVENT, null);started = false;// 关闭管道中的所有阀if (pipeline instanceof Lifecycle) {((Lifecycle) pipeline).stop();}// 关闭所有从属组件if ((loader != null) && (loader instanceof Lifecycle)) {((Lifecycle) loader).stop();}// 触发了特定事件,通知事件监听器lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);}
}

启动类与第五章相比,还是原来的配方,还是原来的味道。

public final class Bootstrap {public static void main(String[] args) {Connector connector = new HttpConnector();Wrapper wrapper1 = new SimpleWrapper();wrapper1.setName("Primitive");wrapper1.setServletClass("PrimitiveServlet");Wrapper wrapper2 = new SimpleWrapper();wrapper2.setName("Modern");wrapper2.setServletClass("ModernServlet");Context context = new SimpleContext();context.addChild(wrapper1);context.addChild(wrapper2);Mapper mapper = new SimpleContextMapper();mapper.setProtocol("http");LifecycleListener listener = new SimpleContextLifecycleListener();((Lifecycle) context).addLifecycleListener(listener);context.addMapper(mapper);Loader loader = new SimpleLoader();context.setLoader(loader);// context.addServletMapping(pattern, name);context.addServletMapping("/Primitive", "Primitive");context.addServletMapping("/Modern", "Modern");connector.setContainer(context);try {connector.initialize();((Lifecycle) connector).start();((Lifecycle) context).start();// make the application wait until we press a key.System.in.read();((Lifecycle) context).stop();} catch (Exception e) {e.printStackTrace();}}
}

启动应用,看后端日志

HttpConnector Opening server socket on all host IP addresses
HttpConnector[8080] Starting background thread
SimpleContextLifecycleListener's event before_start
Starting SimpleLoader
Starting Wrapper Primitive
Starting SimplePipeline
Starting Wrapper Modern
Starting SimplePipeline
Starting SimplePipeline
SimpleContextLifecycleListener's event start
Starting context.
SimpleContextLifecycleListener's event after_start
关闭应用
SimpleContextLifecycleListener's event before_stop
SimpleContextLifecycleListener's event stop
Stopping context.
Stopping SimplePipeline
Stopping wrapper Primitive
Stopping SimplePipeline
Stopping wrapper Modern
Stopping SimplePipeline
Stopping SimpleLoader
SimpleContextLifecycleListener's event after_stopProcess finished with exit code 0

可以看到,随着SimpleContext#start()方法的调用,其下属的各组件和子容器都被启动了起来。而当我往控制台中输入了内容(“关闭应用”)之后,触发关闭流程,随着SimpleContext#stop()方法的调用,其下属各组件与子容器也都被关闭了。由于SimpleContext与SimpleWraper容器实例中都包含SimplePipeline所以你能看到多条SimplePipeline的启动信息。

好,本章内容到此结束。Tomcat通过Lifecycle这么一组接口,将各个组件的生命周期串联了起来,实现了“一键启动”、“一键关闭”。这个思想值得借鉴,体悟这种编程思想,也为我们以后的编程场景多一种参考储备。下一章,我们一起来看看Tomcat中的日志记录器,看看它是怎么记日志的,敬请期待吧!

源码分享

https://gitee.com/huo-ming-lu/HowTomcatWorks

原书中的代码没有明显bug,所以我仅仅格式化了一下代码,并加了些注释

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/322939.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

红米1s 刷入魔趣 (Mokee)ROM(Android 7.1)

目录 背景准备工具硬件&#xff08;自己准备&#xff09;软件&#xff08;我会在文末提供链接&#xff09; 刷机步骤1. 重启电脑2. 安装驱动3. 刷入TWRP4. 清空数据5. 刷入魔趣6. 开机 结尾下载链接 本文由Jzwalliser原创&#xff0c;发布在CSDN平台上&#xff0c;遵循CC 4.0 B…

汇集全球顶级AI的自助平台

1、介绍:此平台以其开放和便捷的特性,为用户提供了一个无需月费的 AI 服务入口。咱可以根据自己的需求,灵活选择和付费使用平台上的 AI 技术。 该平台强调的核心优势在于 “零门槛” 和 “按需付费”,意味着用户不需要进行大额预付或者承担长期的固定费用,而是可以根据实际…

Kubernetes的基本概念

目录 一.基本内容 1.定义 2.作用 二.特性 1.弹性伸缩 2.自我修复 3.服务发现和负载均衡 4.自动发布&#xff08;默认滚动发布模式&#xff09;和回滚 5.集中化配置管理和密钥管理 6.存储编排&#xff0c;支持外挂存储并对外挂存储资源进行编排 7.任务批处理运行 三…

【go项目01_学习记录08】

学习记录 1 模板文件1.1 articlesStoreHandler() 使用模板文件1.2 统一模板 1 模板文件 重构 articlesCreateHandler() 和 articlesStoreHandler() 函数&#xff0c;将 HTML 抽离并放置于独立的模板文件中。 1.1 articlesStoreHandler() 使用模板文件 . . . func articlesSt…

UE5 audio capture 回声问题 ||在安卓上有爆鸣声

参考视频 0.基本步骤 【UE4_蓝图】录制麦克风声音/系统声音并输出保存WAV文件_ue4录音-CSDN博客 1.步骤 1.创建Sound Submix A 2. 右键新建Sound Submix B 3.把B的两个参数调为-96 4.audio capture的Base Submix&#xff0c;把前面提到的A赋值进去 5.开始录制输出和完成录制…

跨越智能建筑桥梁:西门子PLC无缝对接BACnet楼宇自动化系统化

智能楼宇每一个环节的互联互通都至关重要&#xff0c;而PLC&#xff08;可编程逻辑控制器&#xff09;作为自动化领域的基石&#xff0c;其与BACnet协议的融合无疑成为了构建智能楼宇神经系统的关键节点。今天&#xff0c;让我们深入探讨如何利用先进的PLC转BACnet协议网关&…

Java八股文系列之五(分布式事务)

什么是分布式事务 事务是一个程序执行单元&#xff0c;里面的所有操作要么全部执行成功&#xff0c;要么全部执行失败。在分布式系统中&#xff0c;这些操作可能是位于不同的服务中&#xff0c;那么如果也能保证这些操作要么全部执行成功要么全部执行失败呢&#xff1f;这便是…

leetcode-有重复数字的全排列-98

题目要求 思路 1.同【没有重复项的全排列-97】这个题一样&#xff0c;都是递归的题&#xff0c;区别在于这个可能会包含重复的数字&#xff0c;因此&#xff0c;不能只是简单的通过两个值是否相等然后用标志位标记&#xff0c;而是新增了一个数组&#xff0c;这个数组专门用于…

AR人脸道具SDK解决方案,实现道具与人脸的自然融合

AR人脸道具SDK解决方案&#xff0c;实现道具与人脸的自然融合美摄科技以其卓越的技术实力和创新能力&#xff0c;为企业带来了全新的AR人脸道具SDK解决方案。这一解决方案将为企业打开全新的市场机会&#xff0c;为用户带来前所未有的互动体验。 颠覆传统&#xff0c;开启AR人…

Rust Postgres实例

Rust Postgres介绍 Rust Postgres是一个纯Rust实现的PostgreSQL客户端库&#xff0c;无需依赖任何外部二进制文件2。这意味着它可以轻松集成到你的Rust项目中&#xff0c;提供对PostgreSQL的支持。 特点 高性能&#xff1a;Rust Postgres提供了高性能的数据库交互功能&#…

ardupilot的固定翼飞行模式

飞行模式 APM所有的飞行模式都在对应的机型的文件夹下的mode.h里面有定义,针对于不同的模型,功能函数在基类中Mode中都是以纯虚函数实现了, 然后在继承的子类中重新实现它,以实现多态。 takeoff模式 参见网址在 ArduPlane 4.0 及更高版本中,自动起飞本身也是一种模式(…

LeetCode例题讲解:只出现一次的数字

给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算法只使用常量额外空间。 示例 1 &#xff1a; 输入&#xff…

JAVA(三)常用类和API

目录 常用类与基础API---String String的内存结构 构造器和常用方法 字符串构建 String与其他结构间的转换 String的常用API 系列1&#xff1a;常用方法 系列2&#xff1a;查找 系列3&#xff1a;字符串截取 系列4&#xff1a;和字符/字符数组相关 系列5&#xff1a;开头…

JMeter性能压测脚本录制

第一步&#xff1a;电脑打开控制面板设置代理服务器 第二步&#xff1a;jmeter的测试计划添加一个HTTP&#xff08;S&#xff09;脚本记录器 在脚本记录器里配置好信息&#xff0c;然后保存为脚本文件&#xff08;.*表示限定&#xff09; 此方框内容为项目地址&#xff08;可改…

2024好用的4款3D雕刻软件,快来拿走

3D 雕刻是一种让角色、怪物或任何有机形状栩栩如生的方法。您可以将其视为由粘土制成的真实模型&#xff0c;并可以根据您的意愿推、拉、平滑、抓住或捏它以创建其最终形状。3d 雕刻有哪些软件好用&#xff1f;3d 雕刻软件对电脑要求高吗&#xff1f;电脑带不动3d软件怎么办&am…

小程序如何注销

随着移动互联网的深入发展&#xff0c;管控也越来越严格。现在小程序都要求进行ICP备案&#xff0c;不管是新注册的还是以往注册的。很多商家的小程序本身处于无运营状态&#xff0c;现在要求备案&#xff0c;还不如直接注销。下面&#xff0c;将详细介绍小程序注销的步骤和注意…

数学建模资料|历年数维杯数学建模竞赛真题及获奖论文汇总

2024年第九届数维杯大学生数学建模挑战赛&#xff1a;2024年5月10日08:00-5月13日09:00举行&#xff0c;为了更好的帮助参赛同学了解竞赛的赛制及赛题特点&#xff0c;数乐君今天给大家整理了历年数维杯国赛真题及优秀论文&#xff0c;方便同学们赛前巩固训练&#xff0c;掌握解…

【论文阅读】Spectral–Spatial Attention Network for Hyperspectral Image Classification

Spectral–Spatial Attention Network for Hyperspectral Image Classification 论文地址摘要&#xff1a;1. 简介1.1.动机1.2.贡献 2. 相关作品2.1.双向递归网络RNN2.2.CNN2.3. Attention Mechanism 3. 方法3.1 Attention with RNN for Spectral Classification3.2&#xff0e…

【C++】vector的迭代器失效问题(什么是迭代器失效?那些操作会导致迭代器失效?如何避免迭代器失效?)

目录 一、前言 二、什么是迭代器失效&#xff1f; 三、哪些操作会导致迭代器失效&#xff1f; 四、如何避免迭代器失效&#xff1f; &#x1f95d; insert迭代器失效 ✨迭代器失效 ------ 扩容导致的野指针 ✨迭代器失效 ------ 迭代器指向的位置意义发生改变 &#x1f347…