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类,它们的生命周期管理接口和功能接口是分开的,这也符合设计中的接口分离的原则。

你可能感兴趣的:(Tomcat组件生命周期管理)