美文网首页
Tomcat组件生命周期管理

Tomcat组件生命周期管理

作者: 胡飞飞_1995 | 来源:发表于2019-06-02 13:39 被阅读0次

    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组件生命周期管理

          本文链接:https://www.haomeiwen.com/subject/mqdxxctx.html