下面是Tomcat中Lifecycle接口相关的类图,本文讲解使用到了Lifecycle和LifecycleBase类。
Lifecycle处于最顶端的位置,用于对Tomcat所有的组件进行生命周期管理,在这里我们先不讨论和本文无关的Container接口以及MBeanRegistration、JmxEnabled接口,而LifecycleMBeanBase是用于JMX进行管理而设计的类,也不在本文讨论范围之内。
Lifecycle作为所有组件的基类,该接口定义了所有组件都需要实现的方法,如下图,拥有①生命周期中的初始化、启动、停止、销毁方法,②拥有getState和getStateName这两个获得当前对象生命周期状态的方法,③拥有添加监听器和删除监听器的方法。上述的①是把一个组件的生命周期分为了四个步骤,分别是初始化、启动、停止、销毁,所有组件的生命周期都必须经过这样的四个步骤,②则是用于获取组件的生命周期状态,下面我们能够看到tomcat的组件都有一些什么状态,③是给每个组件添加和移除监听器,因为tomcat中每个组件的状态发生变化都需要通知监听器进行处理。
下面来说一说Lifecycle的子类LifecycleBase,LifecycleBase是抽象类,是tomcat中所有组件类的基类,重要程度和Lifecycle相当,因为Lifecycle只是定义了规范,而没有具体实现细节,LifecycleBase则是将规范具体化了,是规范的实践者,他下面还会有很多子类继承他实现了很多功能,所以他有一个承上启下的作用,这个类是相当重要的。那么下面我们一步一步来看LifecycleBase针对上述①②③点都做了什么。
private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();
/**
* The current state of the source component.
*/
private volatile LifecycleState state = LifecycleState.NEW;
首先,LifecycleBase初始化了两个属性,一个是存放监听器的数组,另一个是生命周期状态,初始为NEW。 在这个地方,监听器使用数组存放,是因为可能会有多个不同的监听器在监听组件的生命周期状态,我们要保存这些监听器就需要使用到一个List,这里是关联的关系,每个组件都关联一堆监听器,使用监听器接口来表示一族监听器。(这里使用CopyOnWriteArrayList而不使用一般的ArrayList,是为了解决开发过程中的多线程并发问题,因为对于ArrayList我们不能在一个线程读取他的时候对他进行增删,这样会导致系统发生并发异常。而CopyOnWriteArrayList在增删的时候会copy出一个副本数组来操作,操作完成再将指向旧数组的引用指向这个新的数组,这样其他线程过来读取的时候就依然读取到的是旧的数据,而不会发生异常。)
下面LifecycleState属性则是每个组件必备的属性,每个组件缺少了生命周期状态则不完整,所以这里应该是组合的关系,组件刚加载的时候还没有进行任何初始化操作,所以是NEW状态。LifecycleState是一个枚举类,定义了十二种描述组件生命阶段的状态,如下。
public enum LifecycleState {
NEW(false, null),
INITIALIZING(false, Lifecycle.BEFORE_INIT_EVENT),
INITIALIZED(false, Lifecycle.AFTER_INIT_EVENT),
STARTING_PREP(false, Lifecycle.BEFORE_START_EVENT),
STARTING(true, Lifecycle.START_EVENT),
STARTED(true, Lifecycle.AFTER_START_EVENT),
STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT),
STOPPING(false, Lifecycle.STOP_EVENT),
STOPPED(false, Lifecycle.AFTER_STOP_EVENT),
DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT),
DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT),
FAILED(false, null);
private final boolean available;
private final String lifecycleEvent;
private LifecycleState(boolean available, String lifecycleEvent) {
this.available = available;
this.lifecycleEvent = lifecycleEvent;
}
public boolean isAvailable() {
return available;
}
public String getLifecycleEvent() {
return lifecycleEvent;
}
}
下面添加、查找、删除监听器,没什么好说的,都是ArrayList的基本操作。
@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);
}
下面来了,这是监听器模式中的关键方法,fireLifecycleEvent,报警方法。众所周知,监听器模式由三个要素组成,事件源、事件、监听器,tomcat中LifecycleBase只是一个抽象类,他这个单独的抽象类没有任何作用,什么功能都不能单独完成,他只提供一部分细节的实现以及定义一些规范,所有继承LifecycleBase的组件都是事件源,我们可以使用LifecycleBase来代表所有事件源,而事件则是能够触发监听器进行动作的一个类,比如我们在小明身上放了监听器,小明发出的所有声音都会被我们监听到,在这个例子中,小明就是事件源,而发出的声音则是事件,我们监听的是事件,而不是事件源,因为小明除了发出声音,还可以做看电视等各种事情,这些事情不会被我们监听到,我们没有相应的“看电视监听器”;再比如高铁厕所里的烟雾检测装置,每当有人在厕所内抽烟,报警装置就会启动,在这个例子中,事件源是人,事件则是烟雾,烟雾检测装置是监听器,监听器检测到了烟雾,则启动报警的行为。fireLifecycleEvent中传入两个参数,type和data,方法内部根据这两个参数和当前组件构造出一个LifecycleEvent,然后把这个LifecycleEvent当作参数通知给当前组件中持有的所有监听器。
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);
}
}
所以在tomcat的生命周期监听过程当中,我们监听的事件源是所有组件,而事件则是生命周期状态,当生命周期状态发生改变的时候,我们对这个状态进行监听反应,那么事件则必须持有生命周期状态属性,对应到下面的type类型,tomcat在Lifecycle接口当中使用static final String定义了所有的事件,每次组件进行init/start/stop/destory行为的时候,组件的状态都会发生改变,而每次改变都将触发fireLifecycleEvent方法,new LifecycleEvent的时候,传入第一个参数为当前组件,第二个参数为事件类型,第三个参数为数据(暂时不知道什么情况需要向事件当中传入数据,一般为null),然后调用组件关联到的监听器的监听事件触发方法,传入事件,完成监听动作。
接下来说一说tomcat中模版方法模式的应用
模版方法模式说起来就要简单多了,比如我们要使用一台笔记本电脑,要经过开机、使用电脑、关机三个步骤,不同的人使用电脑的细节不同,但是开机和关机的步骤是相同的,那么我们针对这些一系列的操作,就可以把这一系列操作都放在同一个抽象类的方法里面,所有人都去实现这个方法,下面先贴一段代码
@Override
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
}
上面这个方法是LifecycleBase的init方法,定义了所有组件的初始化步骤,第一步都是先判断是否为NEW状态,如果不是NEW状态,那么抛异常;第二步改变组件的生命周期状态为INITIALIZING,通知监听器进行处理;第三步,调用initInternal方法,进入各个组件的初始化细节;第四步,改变组件的生命周期状态为INITIALIZED,通知监听器进行处理。
protected abstract void initInternal() throws LifecycleException;
在以上的四个步骤当中,我们所有的组件只有第三步进入的方法是不同的,那么我们就在LifecycleBase这个生命周期基类当中定义这个抽象方法,具体细节由不同的子类组件写不同的方法,我们每当想要添加组件的时候,可以直接继承这个LifecycleBase类,重写initInternal方法,就可以完成初始化。
上面四个固定的步骤被每个组件都使用,这些固定的步骤被称为模版,Tomcat中的LifecycleBase类中的init/start/stop/destory方法都使用的是模版方法模式。
以上就是tomcat中使用到的监听器模式和模版方法模式。