【杂谈】Tomcat 之 Lifecycle接口
前言
此篇随笔记录《How Tomcat works》中关于Lifecycle接口的相关总结
Lifecycle接口的主要目的
核心:统一。
已知Tomcat的卡特琳娜(Catalina)由许多组件构成。当Catalina启动的时候,这些组件也要跟着一起启动,并且当Catalina关闭的时候,这些组件也要同时关闭,并且要进行必要的清理操作,释放资源。例如,当容器关闭的时候,需要调用已加载的servlet对象的destroy方法,session对象也要持久化到secondary storage(二级存储,通常指的就是硬盘)。这就要求所有Component有一致的方法,可以统一处理。
如果没有统一会怎么样?
其实也不会怎么样,就是编程起来就比较麻烦。比如有的对象初始化方法叫initiate,有的又叫init。用都可以用,就是不太好用,即你要记住这个对象的初始化方法到底叫什么。而统一之后呢,可以放到一个List中,遍历调用。这不就方便多了嘛。
Lifecycle的事件与监听
Lifecycle,翻译过来就是“生命周期”。那生命周期变化,从一个生命周期进入另一个生命周期,自然就会有相应的事件来表示。比如,人从幼儿长大成人,这是一个事件“成长”,而从中年步入老年,又是另外一个事件“衰老”。那么既然有事件的触发,那就会有事件的监听处理,常见的有两大种。

Lifecycle事件监听器原理

其实就是观察者模式了,熟悉的人就不用看了。关联的类清单如下:
- LifecycleEvent(事件对象)
- Lifecycle 接口
- LifecycleListener 接口
- LifecycleBase => 用于存储并触发监听器,《How Tomcat works》里是比较旧的,对应的是LifecycleSupport类
(1)LifecycleEvent
LifecycleEvent继承于EventObject,EventObject是JDK提供的,它只有一个字段,即Object类型的source,用于表示事件的产生者。
public class EventObject implements java.io.Serializable {
    private static final long serialVersionUID = 5516075349620653480L;
    /**
     * The object on which the Event initially occurred.
     */
    protected transient Object  source;
    public EventObject(Object source) {
        if (source == null)
            throw new IllegalArgumentException("null source");
        this.source = source;
    }
    public Object getSource() {
        return source;
    }
    public String toString() {
        return getClass().getName() + "[source=" + source + "]";
    }
}
public final class LifecycleEvent extends EventObject {
    private static final long serialVersionUID = 1L;
    /**
     *
     *
     * @param lifecycle 指明当前事件在哪个lifecycle组件上产生
     * @param type Event type 事件类型
     * @param data Event data 额外的数据
     */
    public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {
        //把lifecycle赋给source
        super(lifecycle);
        this.type = type;
        this.data = data;
    }
    /**
     *  与此事件关联的事件数据
     */
    private final Object data;
    /**
     * 事件类型
     */
    private final String type;
    public Object getData() {
        return data;
    }
    public Lifecycle getLifecycle() {
        return (Lifecycle) getSource();
    }
    public String getType() {
        return this.type;
    }
}
(2)Lifecycle接口
public interface Lifecycle { 
    //统一的事件类型
    public static final String BEFORE_INIT_EVENT = "before_init";
    public static final String AFTER_INIT_EVENT = "after_init";
    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 static final String AFTER_DESTROY_EVENT = "after_destroy";
    public static final String BEFORE_DESTROY_EVENT = "before_destroy";
    public static final String PERIODIC_EVENT = "periodic";
    public static final String CONFIGURE_START_EVENT = "configure_start";
    public static final String CONFIGURE_STOP_EVENT = "configure_stop";
    //lifecycle表示的是一个组件,故可以在组件上添加监听器
    public void addLifecycleListener(LifecycleListener listener);
    //获取注册在此组件上的监听器
    public LifecycleListener[] findLifecycleListeners();
    //移除某个监听器
    public void removeLifecycleListener(LifecycleListener listener);
    //统一的生命周期方法
    public void init() throws LifecycleException;
    public void start() throws LifecycleException;
    public void stop() throws LifecycleException;
    public void destroy() throws LifecycleException;
    //获取组件状态
    public LifecycleState getState();
    public String getStateName();
    public interface SingleUse {
    }
}
(3)LifecycleListener接口
public interface LifecycleListener {
    //接收事件对象作为参数
    //在方法体内,根据事件类型做自定义的响应
    public void lifecycleEvent(LifecycleEvent event);
}
(4)LifecycleBase 抽象类
一般组件都以此为父类。主要看存监听器的List和fireLifecycleEvent方法。以下把大部分代码省略了,感兴趣的可自行翻阅源码。
public abstract class LifecycleBase implements Lifecycle {
    private static final Log log = LogFactory.getLog(LifecycleBase.class);
    private static final StringManager sm = StringManager.getManager(LifecycleBase.class);
    //用于存储注册在此组件上的监听器
    private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();
    //组件状态
    private volatile LifecycleState state = LifecycleState.NEW;
    private boolean throwOnFailure = true;
    public boolean getThrowOnFailure() {
        return throwOnFailure;
    }
    public void setThrowOnFailure(boolean throwOnFailure) {
        this.throwOnFailure = throwOnFailure;
    }
    @Override
    public void addLifecycleListener(LifecycleListener listener) {
        lifecycleListeners.add(listener);
    }
    @Override
    public LifecycleListener[] findLifecycleListeners() {
        return lifecycleListeners.toArray(new LifecycleListener[0]);
    }
    @Override
    public void removeLifecycleListener(LifecycleListener listener) {
        lifecycleListeners.remove(listener);
    }
    //触发事件,告知所有监听器。
    protected void fireLifecycleEvent(String type, Object data) {
        LifecycleEvent event = new LifecycleEvent(this, type, data);
        for (LifecycleListener listener : lifecycleListeners) {
            listener.lifecycleEvent(event);
        }
    }
    @Override
    public final synchronized void init() throws LifecycleException {
        //...省略
    }
    protected abstract void initInternal() throws LifecycleException;
    @Override
    public final synchronized void start() throws LifecycleException {
        //...省略
    }
    protected abstract void startInternal() throws LifecycleException;
    @Override
    public final synchronized void stop() throws LifecycleException {
        //...省略
    }
    protected abstract void stopInternal() throws LifecycleException;
    @Override
    public final synchronized void destroy() throws LifecycleException {
        //...省略
    }
    protected abstract void destroyInternal() throws LifecycleException;
    @Override
    public LifecycleState getState() {
        return state;
    }
    @Override
    public String getStateName() {
        return getState().toString();
    }
    protected synchronized void setState(LifecycleState state) throws LifecycleException {
        setStateInternal(state, null, true);
    }
    protected synchronized void setState(LifecycleState state, Object data)
            throws LifecycleException {
        setStateInternal(state, data, true);
    }
    private synchronized void setStateInternal(LifecycleState state, Object data, boolean check)
            throws LifecycleException {
        //...省略
    }
    private void invalidTransition(String type) throws LifecycleException {
        //...省略
    }
    private void handleSubClassException(Throwable t, String key, Object... args) throws LifecycleException {
        //...省略
    }
}
【杂谈】Tomcat 之 Lifecycle接口的更多相关文章
- Tomcat8源码笔记(一)Lifecycle接口
		第一次阅读Tomcat8源码,就以Lifecycle作为笔记阅读的开篇吧,一千个读者就有一千个哈姆雷特,每个人都Tomcat的理解都不同,如果不记录一次Tomcat源码可能忘了就忘了. 断断DEBUG ... 
- 0001 - Spring 框架和 Tomcat 容器扩展接口揭秘
		前言 在 Spring 框架中,每个应用程序上下文(ApplicationContext)管理着一个 BeanFactory,BeanFactory 主要负责 Bean 定义的保存.Bean 的创建. ... 
- 本地tomcat调用远程接口报错:java.lang.reflect.InvocationTargetException
		今天碰到一个奇怪的问题,本地Eclipse起了一个tomcat通过http去调一个外部接口,结果竟然报了一个反射的异常,先看下完整日志: , :: 下午 org.apache.catalina.sta ... 
- java web项目war包部署,使用tomcat对指定接口设置身份认证
		先简单说一下需求: 将一个基于springboot2.0开发的java web项目打成war包,通过tomcat部署到一台linux服务器上,项目相关的一些图片等资源也按照一定规则放置在服务器构建好的 ... 
- IDEA部署Tomcat应用所有接口中文乱码
		解决问题的思路: 1.分析比对http请求头,contentType等设置 2.前段编码,后端解码,这个方式比较落麻烦,凡是有中文乱码的地方都要进行解决 3.修改Tomcat的默认编码,tomcat8 ... 
- 怎么用tomcat对socket接口的程序进行调试
		对socket(套接字)的demo方法大多都是用main方法进行测试的,那如何用tomcat进行测试呢? 这里需要借助一个工具:工具的名字是:Sockettool.exe .下图是该工具的内容.连上监 ... 
- 浅谈Tomcat和Servlet
		本文浅谈下对Tomcat和Servlet总体的理解,初学时有用过一段时间,但当时疲于应对如何xml配置和使用,对他们的理解就像是一个黑匣子.现在回顾一下帮助自己加深网络的理解.开始还是先推荐我看的文章 ... 
- 深入分析理解Tomcat体系结构
		Tomcat整体结构 由上图可知Tomcat的顶层容器是Server,而且一个Tomcat对应一个Server,一个server有多个service提供服务.service包含两个重要组件:Conne ... 
- Tomcat源码分析 (七)----- Tomcat 启动过程(二)
		在上一篇文章中,我们分析了tomcat的初始化过程,是由Bootstrap反射调用Catalina的load方法完成tomcat的初始化,包括server.xml的解析.实例化各大组件.初始化组件等逻 ... 
随机推荐
- rocket-mq  windows下载安装
			内容来源:https://www.jianshu.com/p/4a275e779afa 1环境 JDK1.8.Maven.Git 2安装部署 1.下载 1.1地址:http://rocketmq ... 
- CSS基础一
			css作用 css将内容和样式相分离,便于修改样式.HTML 写网页的内容,CSS写内容的样式 CSS构成 p{ /*p为标签,也可以称为选择器,选择包住的内容的格式*/ font-size:12px ... 
- 小白的CTF学习之路7——内存与硬盘
			前天去网吧跟朋友包宿,导致昨天一整天都报废,今天早上研究了一下nethunter导致手机成功变砖,感冒不停地咳嗽,这些理由应该足够我前两天拖更了吧,下面开始正题 磁盘学习路线 虚拟缓存 虚拟内存 节约 ... 
- UGUI小技巧之Text随文本内容自动变化大小
			看了网上很多帖子,都是说在 Text 上面加上 Content Size Fitter 组件,并将对应的轴向改成 Preferred size 就可以实现 Text 大小随着文本内容自适应,如下图: ... 
- 默认空间和webapps下项目部署
			用eclipse默认的工作区和webapps的细节 用{WORKSPACE}表示你的eclipse的工作空间根目录,然后你打开 {WORKSPACE}\.metadata\.plugins\org.e ... 
- Java工具eclipse控制台console输出乱码问题
			捣鼓了一下午,终于tm解决! 我的是Scanner读入,println打印乱码问题. 首先在cmd窗口运行java,是没有乱码问题的,这证明了在cmd窗口时Scanner输入的和println打印的编 ... 
- Linux命令中:rsync和scp之间的区别
			scp是把文件全部复制过去,当文件修改后还是把所有文件复制过去, rsync 第一次是把所有文件同步过去,当文件修改后,只把修改的文件同步过去 rsync -av 10.251.205.8:/usr1 ... 
- android 图片内存管理
			图片对象: drawable bitmap etc.图片对象在Android上该缓存吗?什么时候缓存?怎么缓存?缓存后使用时怎么取出?怎么销毁?什么时候销毁? bitmap对象(new出来的) :需要 ... 
- Python机器学习步骤
			推荐学习顺序 学习机器学习得有个步骤, 下面大家就能按照自己所需, 来探索这个网站. 图中请找到 "Start", 然后依次沿着箭头, 看看有没有不了解/没学过的地方, 接着, 就 ... 
- hive 日常技巧
			--删除表中重复数据 delete from vitae a where (a.peopleId,a.seq) in (select peopleId,seq from vitae group by ... 
