第一次阅读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. spring mvc+mybatis 构建 cms + 实现UC浏览器文章功能

    最近公司在模拟UC浏览器做一个简单的cms系统,主要针对于企业内部的文章浏览需求,这边考虑用户大多用mobile浏览文章内容,故使用原生的ios和android进行开发,后面也会集成html5. 1. ...

  2. javaScript 字符串

    var name = '小明'; var age = 20; var message = '你好, ' + name + ', 你今年' + age + '岁了!'; alert(message) 要 ...

  3. 【NIFI】 Apache NiFI 安装及简单的使用

    NiFI介绍 NiFi(NiagaraFiles)是为了实现系统间数据流的自动化而构建的.虽然术语“数据流”用于各种上下文,但我们在此处使用它来表示系统之间的自动和管理信息流 官网地址:http:// ...

  4. 从中央仓库下载所想要的jar包

    中央仓库地址:https://mvnrepository.com/ 这边我搜索一个commons-logging包作为例子: 点击下面第二个绿色的comons-logging进入这个页面: 一.win ...

  5. winform改变启动界面

    我们知道,有时做个小项目什么的,一般从登录开始,再到主页,再到其他业务,如果做到其他页面功能,调试时还要从登录页面一个个点进去,明显的降低开发进度. 这时,我们可以直接将目标界面改为启动页面即可. u ...

  6. Linux下设置快捷键

    以设置终端为例,进入Settings>>Keyboard>>Custom Shortcuts,点左下脚的+号,Name栏填入Treminal,command栏填入gnome-t ...

  7. js实现抽奖

    抽奖.html: <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  8. 使用pwm进行呼吸灯的设计

    本文源码已经上传至csdn: 程序源码如下; #define LEDC_IO_0 (2) esp_err_t app_main() { ledc_timer_config_t ledc_timer = ...

  9. DDD简明入门之道 - 开篇

    DDD简明入门之道 - 开篇 犹豫了很久才写下此文,一怕自己对DDD的理解和实践方式有偏差,二怕误人子弟被贻笑大方,所以纰漏之处还望各位谅解.不啰嗦,马上进入正题,如果你觉得此文不错就点个赞吧. 概述 ...

  10. Spark中的Phoenix Dynamic Columns

    代码及使用示例:https://github.com/wlu-mstr/spark-phoenix-dynamic phoenix dynamic columns HBase的数据模型是动态的,很多系 ...