Tomcat组件生命周期管理
LifeCycle接口
每个组件都要经历创建、初始化、启动这个几个过程,这些状态以及状态的转化是不变的。变化点是每个具体组件的初始化方法,也就是启动方法是不一样的。
我们把这些不变点抽象为一个接口,来控制整个生命周期,就是LifeCycle(org.apache.catalina.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";
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 {
}
}
我们可以看到,LifeCycle接口里有init()、start()、stop()和destory()方法,每个具体的组件会去实现这些方法。
在父组件的init()方法中会创建子组件,并调用子组件的init()方法。因此调用者可以无差别的调用各组件的init()方法,这就是组合模式的使用。并且只要调用最顶层组件,也就是Server组件的init()和start()方法,整个Tomcat就被启动起来了。
可扩展性:Lifecycle事件
因为各个组件init()和start()方法的具体实现是复杂多变的,比如在Host容器的启动方法里需要扫描wwbapps目录下的Web应用,创建相应的Context容器,如果将来需要增加新的逻辑,直接修改start()方法?这就会违反开闭原则。
我们发现,组件的init()和start()调用是由它的父组件的状态变化触发的,上层组件的初始化会触发子组件的初始化,上层组件的启动会触发子组件的启动,因此我们把组件的生命周期定义成一个个状态,把状态的转变看做成一个事件。而事件是有监听器的,在监听器里可以实现一些逻辑,并且监听器也可以方便的添加和删除,这就是典型的观察者模式。
具体来说就是在Lifecycle接口中加入两个方法:添加监听器和删除监听器,还需要定义一个Enum来表示组件有哪些转态,以及处在什么状态会触发什么事件。这就是枚举类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;
}
}
从上面代码可以看到,组件的生命周期有NEW、INITIALIZING、INITIALIZED等,一旦组件达到相应的状态就触发相应的事件,比如NEW状态表示组件刚刚被实例化;而当init()方法被调用时,状态就变成了INITIALIZING状态,这个时候,就会触发BEFORE_START_EVENT事件,如果有监听器在监听这个事件,它的方法就会被调用。
重用性:LifecycleBase抽象基类
Tomcat定义了一个基类LifecycleBase来实现Lifecycle接口,把一些公共的逻辑放到基类中去,比如生命状态的转变与维护、生命事件的触发以及监听器的添加和删除等,而子类就负责实现自己的初始化、启动和停止等方法。为了避免跟基类中的方法同名,我们把具体子类的实现方法改个名字,在后面加上Internal,叫initInternal()、startInternal()等。LifCycleBase:
public abstract class LifecycleBase implements Lifecycle {
@Override
public final synchronized void init() throws LifecycleException {
// 1.状态检查
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
// 2.触发 INITIALIZING 事件的监听
setStateInternal(LifecycleState.INITIALIZING, null, false);
// 3.调用具体子类的初始化方法
initInternal();
// 4.触发 INITIALIZED 事件的监听器
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);
}
}
protected abstract void initInternal() throws LifecycleException;
protected abstract void startInternal() throws LifecycleException;
protected abstract void stopInternal() throws LifecycleException;
protected abstract void destroyInternal() throws LifecycleException;
}
LifecycleBase实现了Lifecycle接口中的所有方法,还定义了相应的抽象方法交给具体子类去实现,这是典型的模板设计模式。
我们来看看init()方法的实现:
- 第一步,检查状态的合法性,比如当期那状态必须是NEW然后才能初始化
- 第二步,触发 INITIALIZING 事件的监听器:
setStateInternal(LifecycleState.INITIALIZING, null, false);
在这个setStateInternal方法里,会调用监听器的业务代码。
- 第三步,调用具体子类实现的抽象方法initInternal()方法。为了实现一键式启动,具体组件在实现initInternal()方法时,又会调用它的子组件的init()方法。
- 第四步,子组件初始化后,触发 INITIALIZED 事件的监听器,相应监听器的业务方法就会被调用
setStateInternal(LifecycleState.INITIALIZED, null, false);
总之,LifecycleBase调用了抽象方法来实现骨架逻辑。那监听器的注册是怎么实现的呢?分为两种情况:
- Tomcat自定义了一些监听器,这些监听器是父组件在创建子组件的过程中注册到子组件的。比如 MemoryLeakTrackingListener 监听器,用来检测 Context 容器中的内存泄漏,这个监听器是Host容器在创建Context容器时注册到Context中的。
- 我们还可以在server.xml中定义自己的监听器,Tomcat在启东时会解析server.xml,创建监听器并注册到容器组件。
生命周期管理总体类图
Lifecycle类图.jpeg图中的StandardServer、StandardService等是Server和Service组件的具体实现类,他们都继承了LifecycleBase。
StandardEngine、StandardHost、StandardContext和StandardWrapper是相应容器组件的具体实现类,因为他们都是容器,所以继承了ContainerBase抽象基类,而ContainerBase实现了Container接口,也继承了LifecycleBase类,它们的生命周期管理接口和功能接口是分开的,这也符合设计中的接口分离的原则。