第一次阅读Tomcat8源码,就以Lifecycle作为笔记阅读的开篇吧,一千个读者就有一千个哈姆雷特,每个人都Tomcat的理解都不同,如果不记录一次Tomcat源码可能忘了就忘了. 断断DEBUG了几天,决定从Lifecycle开始记录.

LifeCycle接口定义如下:

个人理解:Lifecycle接口定义了生命周期描述的字符常量,初始化之前、初始化之后、启动、启动之前、启动之后、停止、停止之前、停止之后、销毁之前、销毁之后等等;定义了生命周期监听器的三个方法,添加监听器、获得组件上监听器以及移除监听器;定义了组件的四个方法,初始化、启动、停止、销毁;定义了获取当前组件生命状态、生命状态名称;

LifecycleState是枚举,描述着声明周期的状态

Lifecycle接口实现类中比较重要LifecycleBase,以及LifecycleSupport类.

三者之间关系大概这样:LifecycleBase继承Lifecycle,且持有LifecycleSupport属性,LifecycleSupport也持有Lifecycle属性.

LifecycleBase抽象类

LifecycleBase持有LifecycleSupport实例lifecycle,而lifecycle本身持有的是Lifecycle类型,也就意味着如果有一个LifecycleBase的实例A,那A的lifecycle属性也持有A,我有你,你也有我. LifecycleBase实现了Lifecycle的监听器的方法,全部是对LifecycleSupport进行操作;LifecycleBase包括实现类默认创建的时候状态state都是LifecycleState.NEW.

LifecycleSupport类

LifecycleSupport持有Lifecycle实例,以及一个LifecycleListener集合listeners,所有添加、查找所有监听器、移除监听器、触发监听事件都是通过listeners来完成.

Tomcat的Lifecycle,如果要说设计模式,我觉得 观察者模式应该合适些.  观察者模式又被称为源-收听者模式(Listener),百度百科这么介绍该设计模式 : 一个目标物件管理所有依赖于他的观察者物件,并且在他本身状态发生改变时主动发出通知,通常用于事件处理系统。所以,这是我觉得Lifecycle可以称为观察者模式的原因,下面是Tomcat中StandardServer启动的过程,fireLifecycleEvent,Server主动去通知它的LifecycleListener,执行相应处理逻辑。

简单写个类似的例子,描述下我认为的观察者模式,一个考试Exam,一个闹钟类RingBell,闹钟一直在走,当闹钟到11.25分就去通知考试开始,中间过程省略一直到收卷结束。

Exam考试类

public class Exam implements Lifecycle {
public void init() { System.out.println("监考老师分发考卷"); }
public void start() { System.out.println("监考老师: 考试开始,大家可以做题了"); }
public void stop() { System.out.println("监考老师:时间到,停止答题"); }
public void destroy(){ System.out.println("监考老师收完卷子走人"); } private LifecycleSupport lifecycle=new LifecycleSupport(this);
public void addLifecycleListener(final LifecycleListener listener) {
lifecycle.addLifecycleListener(listener);
} public LifecycleListener[] findLifecycleListeners() {
return lifecycle.findLifecycleListeners();
} public void removeLifecycleListener(final LifecycleListener listener) {
lifecycle.removeLifecycleListener(listener);
}
public LifecycleState getState() {
return null;
}
public String getStateName() {
return null;
}
}

RingBell类

public class RingBell implements Lifecycle {

    private boolean flag=true;
public void setFlag(final boolean flag) {
this.flag = flag;
} private LifecycleSupport lifecycle=new LifecycleSupport(this);
public void addLifecycleListener(final LifecycleListener listener) {
lifecycle.addLifecycleListener(listener);
}
public LifecycleListener[] findLifecycleListeners() {
return lifecycle.findLifecycleListeners();
}
public void removeLifecycleListener(final LifecycleListener listener) {lifecycle.removeLifecycleListener(listener); }
public void start() throws LifecycleException { }
public void stop() throws LifecycleException { }
public void destroy() throws LifecycleException {}
public LifecycleState getState() { return null; }
public String getStateName() { return null; } public void init() throws LifecycleException {
System.out.println("时钟慢慢走动.....");
while(flag){
Date now = new Date();
System.out.println(new SimpleDateFormat("hh:mm:ss").format(now));
if(now.getHours()==11 && now.getMinutes()== 25 && now.getSeconds()==0){
lifecycle.fireLifecycleEvent(Lifecycle.AFTER_INIT_EVENT,null);
}else if(now.getHours()==11 && now.getMinutes()== 25 && now.getSeconds()== 2){
lifecycle.fireLifecycleEvent(Lifecycle.START_EVENT,null);
}else if(now.getHours()==11 && now.getMinutes()== 25 && now.getSeconds()==4){
lifecycle.fireLifecycleEvent(Lifecycle.STOP_EVENT,null);
}else if(now.getHours()==11 && now.getMinutes()== 25 && now.getSeconds()== 6){
lifecycle.fireLifecycleEvent(Lifecycle.AFTER_DESTROY_EVENT,null);
}else if(now.getHours()==11 && now.getMinutes()== 25 && now.getSeconds()== 8){
lifecycle.fireLifecycleEvent(Lifecycle.AFTER_DESTROY_EVENT,null);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("谁把时钟关了");
} }

ExamListener

public class ExamListener implements LifecycleListener {

    private Exam exam;
public ExamListener(final Exam exam) {
this.exam = exam;
}
public void lifecycleEvent(final LifecycleEvent event) {
if(event.getType().equals(Lifecycle.AFTER_INIT_EVENT)){
exam.init();
}else if(event.getType().equals(Lifecycle.START_EVENT)){
exam.start();
}else if(event.getType().equals(Lifecycle.STOP_EVENT)){
exam.stop();
}else if(event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)){
exam.destroy();
RingBell ringBell= (RingBell) event.getLifecycle();
ringBell.setFlag(false);
}
}
}

测试方法:

 public static void main(String[] args) throws LifecycleException {
Exam exam = new Exam();
RingBell ringBell=new RingBell();
ringBell.addLifecycleListener(new ExamListener(exam));
ringBell.init();
}

解释说明: RingBell作为被观察对象,观察对象就是ExamListener, RingBell持有观察对象ExamListener,当RingBell状态改变(当前时间改变),就去通知观察者们,观察者们就触发事件,开始考试啊、结束考试等等;

这里的观察者不是具象的观察者对象,而是监听器类型LifecycleListener,通过监听器来使Exam做出响应处理,事件驱动机制。测试效果如下:

注意 RingBell对象 不合适 直接持有Exam的引用, 耦合性太高,假如再来一个观察者监考老师,监考老师需要考试期间来巡查,那我们只需要添加一个LifecycleListener实现类即可,不需要让RingBell持有这个监考老师监听器。

Lifecycle代表了Tomcat的组件的生命周期,定义过init、start、stop、destroy等四个重要的方法。而Tomcat的重要组件全部都实现了LifecycleBase接口,继承了LifecycleBase接口,下图是利用IDEA画出来的继承关系:

LifecycleBase抽象类采用了模板设计模式,听说过采用了模板设计模式的有Arrays.sort方法、Servlet的service方法,模板设计模式的好处就是 统一按照模板执行,共性的基础上个性的地方,再各自处理。

LifecycleBase的init模板方法:

state默认为LifecycleState.NEW,所以第一步的目的是为了防止多次调用init方法,Tomcat组件基本上初始化一次就够了。

init模板共性的地方就是设置当前组件状态,LifecycleState.INITIALIZING以及LifecycleState.INITIALIZED,个性之处在于initInternal,不同组件实现逻辑就是这里体现的。

而共性的设置组件状态的目的呢?接着往下看。

设置组件状态,check为true会再次校验组件状态,一般check都为false.  更新组件的状态,之后就是观察者模式的应用,主动通知组件上左右的监听器LifecycleListener,触发相应的事件。

通过上面两段代码分析,一个组件init初始化大致过程:组件上所有监听器,如果有的话触发LifecycleState.INITIALIZING,也就是before_init,  接着initInternal,就是组件自己会重写的方法,接着触发组件上监听器的LifecycleState.INITIALIZED,也就是after_init类型事件!

LifecycleBase的start模板方法

首先检查组件状态,如果已经处于启动相关状态,before_start/after_start/start状态的话,打印日志并且不再启动;

组件如果还没初始化呢,那赶紧初始化;组件初始化、启动失败了,赶紧停止组件;到这里,如果组件不是初始化完成或者停止的状态,那就有问题了,启动中?启动了? 正要启动组件,你启动好了,抛出异常;

上述检查都没问题,那设置状态LifecycleState.STARTING_PREP,也就是before_start,目的不用多说了吧,启动组件上监听器,触发before_start的事件啊,接着才是各个组件个性的startInternal,启动完成没有问题,那就设置状态LifecycleState.STARTED,也就是after_start,目的同样启动组件监听器触发after_start事件。

剩下两个stop 、destory模板方法,不用我说肯定是类似的,这里就不多此一举了,毕竟我们连Tomcat启动干了啥都不知道,相比于分析停止、摧毁的模板方法,还是留时间分析Tomcat的启动吧。

总结

感觉自己这样一整理舒畅多了,见识两种设计模式,也请求了Tomcat的组件的初始化、启动流程要干啥,从哪里分析了,核心就是Lifecycle接口以及LifecycleBase以及LifecycleListener,接下来,只需要关注组件上有哪些监听器、初始化initInternal、启动startInternal过程干了啥。

Tomcat8源码笔记(一)Lifecycle接口的更多相关文章

  1. Tomcat8源码笔记(七)组件启动Server Service Engine Host启动

    一.Tomcat启动的入口 Tomcat初始化简单流程前面博客介绍了一遍,组件除了StandardHost都有博客,欢迎大家指文中错误.Tomcat启动类是Bootstrap,而启动容器启动入口位于 ...

  2. Tomcat8源码笔记(四)Server和Service初始化

    上一章 简单说明下Tomcat各个组件: Server:服务器,Tomcat服务器,一个Tomcat只有一个Server组件; Service:业务层,是Server下最大的子容器,一个Server可 ...

  3. Tomcat8源码笔记(五)组件Container分析

    Tomcat8源码笔记(四)Server和Service初始化 介绍过Tomcat中Service的初始化 最先初始化就是Container,而Container初始化过程是咋样的? 说到Contai ...

  4. Tomcat8源码笔记(三)Catalina加载过程

    之前介绍过 Catalina加载过程是Bootstrap的load调用的  Tomcat8源码笔记(二)Bootstrap启动 按照Catalina的load过程,大致如下: 接下来一步步分析加载过程 ...

  5. Tomcat8源码笔记(八)明白Tomcat怎么部署webapps下项目

    以前没想过这么个问题:Tomcat怎么处理webapps下项目,并且我访问浏览器ip: port/项目名/请求路径,以SSM为例,Tomcat怎么就能将请求找到项目呢,项目还是个文件夹类型的? Tom ...

  6. Tomcat8源码笔记(六)连接器Connector分析

    根据 Tomcat8源码笔记(五)组件Container分析 前文分析,StandardService的初始化重心由 StandardEngine转移到了Connector的初始化,本篇记录下Conn ...

  7. Tomcat8源码笔记(二)Bootstrap启动

    TOMCAT源码调试入口是Bootstrap类的main方法,我的启动参数VM: -Dcatalina.home=E:/Tomcat_Source_Code/apache-tomcat-8.0.53- ...

  8. Tomcat8源码笔记(九)组件StandardContext启动流程--未完待续

    StandardContext代表的是webapps下项目,一个项目就是一个StandardContext,作为Tomcat组件的一部分,就会实现Lifecycle接口,被Tomcat管理着生命周期, ...

  9. redis源码笔记(一) —— 从redis的启动到command的分发

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载联系作者并保留声明头部与原文链接https://luzeshu.com/blog/redis1 本博客同步在http://www.cnblog ...

随机推荐

  1. laravel config 配置无效

    修改了配置文件config  发现逻辑代码中并无生效. 猜测缓存,所以执行下: php artisan config:cache 缓存文件默认会存在bootstrap/cache 中,并不在stora ...

  2. Unable to instantiate Action, xxxAction, defined for 'xxxAction' in namespace '/'xxx

    最近写SSH2的项目时,遇到一些小问题,action得不到service实例,遂将struct2委托给spring进行管理,然后修改了bean的id和action的class,但是运行后发现找不到ac ...

  3. PowerShell工作流学习-2-工作流运行Powershell命令

    关键点: a)inlineScript 活动具有活动通用参数,但不具有PowerShell 通用参数,且inlineScript 脚本块中的命令和表达式不具有工作流的功能b)默认inlineScrip ...

  4. 源码分析MySQL mysql_real_query函数

    目录 目录 1 1. 前言 1 2. 调用路径 2 3. MAX_PACKET_LENGTH宏 2 4. DBUG_RETURN宏 3 5. COM_QUERY枚举值 3 6. mysql_query ...

  5. VP-UML系统建模工具研究

    一.基本信息 标题:VP-UML系统建模工具研究 时间:2014 出版源:软件工程师 领域分类:面向对象:CASE:UML:系统建模: 二.研究背景 问题定义:VP-UML系统建模的主要特点 难点:运 ...

  6. VirtualBox中的快捷键

    VirtualBox中的快捷键 VirtualBox中的 Host 键默认是: Right Ctrl 键,意思是键盘上右边那个 “Ctrl”键. Host 键可以点击 Oracle VM Virtua ...

  7. Java学习笔记46(多线程三:线程之间的通信)

    多个线程在处理同一个资源,但是线程的任务却不相同,通过一定的手段使各个线程能有效地利用资源, 这种手段即:等待唤醒机制,又称作线程之间的通信 涉及到的方法:wait(),notify() 示例: 两个 ...

  8. apache环境之困扰,Rewrite导致无法加载多个不同的.html文件

    又是一个项目,为访问多个纯静态html页面h5游戏页,能够做一些简单分享和跳转即可.原本是一个简单得不能的项目,但是却多生了事端. 我按照apache的惯例,将文件上传到服务器的DocumentRoo ...

  9. ElasticSearch权威指南学习(映射和分析)

    概念 映射(mapping)机制用于进行字段类型确认,将每个字段匹配为一种确定的数据类型(string, number, booleans, date等).+ 分析(analysis)机制用于进行全文 ...

  10. System.InvalidOperationException: 可为空的对象必须具有一个值。

    Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[0]      An unhandled exception has ...