Tomcat源码篇-生命周期的架构设计

2022-04-13 16:14:33 浏览数 (1)

Tomcat生命周期

  在上篇文章中我们看到了Tomcat架构中的核心组件,而且各个组件都有各自的作用,各司其职,而且相互之间也有对应的父子关系,那么这些对象的创建,调用,销毁等操作是怎么处理呢?

  也就是在Tomcat中的组件的对象生命周期是怎么管理的呢?针对这个问题,在Tomcat中设计了Lifecycle接口来统一管理Tomcat中的核心组件的生命周期,所以本文我们就系统的来介绍下Lifecycle接口的设计

一、LifeCycle接口设计

  为了统一管理Tomcat中的核心组件的生命周期,而专门设计了LifeCycle接口来统一管理,我们来看看在LifeCycle接口中声明了哪些内容。

1.生命周期的方法

  在LifeCycle中声明了和生命周期相关的方法,包括init(),start(),stop(),destory()等方法。

  在声明的方法执行的过程中会涉及到对应的状态的转换,在LifeCycle接口的头部文档中很清楚的说了。

2.相关的状态处理

  通过上图我们可以很清楚的看到相关的方法执行会涉及到的相关状态的转换,比如init()会从New这个状态开始,然后会进入 INITIALIZING 和 INITIALIZED 等。因为这块涉及到了对应的状态转换,在Lifecycle中声明了相关的状态和事件的生命周期字符串。

代码语言:javascript复制
    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";


    /**
     * The LifecycleEvent type for the "periodic" event.
     * 周期性事件(后台线程定时执行一些事情,比如:热部署、热替换)
     */
    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";

在LifecycleState中建立了对应关系

  针对特定的事件就会有相关的监听器来监听处理。在Lifecycle中定义了相关的处理方法。

代码语言:javascript复制
    public void addLifecycleListener(LifecycleListener listener);

    public LifecycleListener[] findLifecycleListeners();

    public void removeLifecycleListener(LifecycleListener listener);

  通过方法名称我们就能很清楚该方法的相关作用,就不过程介绍了。然后来看下对应的监听器和事件接口的对应设计。

二、监听器和事件的设计

  接下来看下LifecycleListener的设计。其实代码非常简单。

代码语言:javascript复制
public interface LifecycleListener {


    /**
     * Acknowledge the occurrence of the specified event.
     *  触发监听器后要执行逻辑的方法
     * @param event LifecycleEvent that has occurred
     */
    public void lifecycleEvent(LifecycleEvent event);


}

  然后来看下事件的接口

代码语言:javascript复制
public final class LifecycleEvent extends EventObject {

    private static final long serialVersionUID = 1L;


    /**
     * Construct a new LifecycleEvent with the specified parameters.
     *
     * @param lifecycle Component on which this event occurred
     * @param type Event type (required)
     * @param data Event data (if any)
     */
    public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {
        super(lifecycle); // 向上转型,可接受一切实现了生命周期的组件
        this.type = type;
        this.data = data;
    }


    /**
     * The event data associated with this event.
     * 携带的额外的数据,传递给监听器的数据
     */
    private final Object data;


    /**
     * The event type this instance represents.
     * 事件类型
     */
    private final String type;


    /**
     * @return the event data of this event.
     */
    public Object getData() {
        return data;
    }


    /**
     * @return the Lifecycle on which this event occurred.
     */
    public Lifecycle getLifecycle() {
        return (Lifecycle) getSource();
    }


    /**
     * @return the event type of this event.
     */
    public String getType() {
        return this.type;
    }
}

  也是非常简单,不过多的赘述。

三、LifecycleBase

  通过上面的介绍我们可以看到在Tomcat中设计了Lifecycle和LifecycleListener和LifecycleEvent来管理核心组件的生命周期,那么我们就需要让每一个组件都实现相关的接口。这时你会发现交给子类的工作量其实是比较大的,不光要完成各个组件的核心功能,还得实现生命周期的相关处理,耦合性很强,这时在Tomcat中给我们提供了一个LifecycleBase的抽象类,帮助我们实现了很多和具体业务无关的处理,来简化了具体组件的业务。

1.事件处理

  在上面的接口设计中对于监听对应的事件处理是没有实现的,在LifecycleBase把这块很好的实现了,我们来看下。首先定义了一个容器来存储所有的监听器

代码语言:javascript复制
// 存储了所有的实现了LifecycleListener接口的监听器 
private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();

  同时提供了触发监听的相关的方法,绑定了对应的事件。

代码语言:javascript复制
    /**
     * Allow sub classes to fire {@link Lifecycle} events.
     *     监听器触发相关的事件
     * @param type  Event type  事件类型
     * @param data  Data associated with event.
     */
    protected void fireLifecycleEvent(String type, Object data) {
        LifecycleEvent event = new LifecycleEvent(this, type, data);
        for (LifecycleListener listener : lifecycleListeners) {
            listener.lifecycleEvent(event);
        }
    }

 &emsp已经针对Listener相关的处理方法

代码语言:javascript复制
    // 添加监听器
    @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);
    }

2.生命周期方法

  在LifecycleBase中最核心的还是实现了Lifecycle中的生命周期方法,以init方法为例我们来看。

代码语言:javascript复制
    /**
     * 实现了 Lifecycle 中定义的init方法
     * 该方法和对应的组件的状态产生的关联
     * @throws LifecycleException
     */
    @Override
    public final synchronized void init() throws LifecycleException {
        if (!state.equals(LifecycleState.NEW)) {
            // 无效的操作  只有状态为 New 的才能调用init方法进入初始化
            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
        }

        try {
            // 设置状态为初始化进行中....同步在方法中会触发对应的事件
            setStateInternal(LifecycleState.INITIALIZING, null, false);
            initInternal(); // 交给子类具体的实现 初始化操作
            // 更新状态为初始化完成 同步在方法中会触发对应的事件
            setStateInternal(LifecycleState.INITIALIZED, null, false);
        } catch (Throwable t) {
            handleSubClassException(t, "lifecycleBase.initFail", toString());
        }
    }

源码解析:

  1. 我们看到首先会判断当前对象的state状态是否为NEW,因为init方法只能在NEW状态下才能开始初始化
  2. 如果1条件满足则会更新state的状态为INITIALIZED 同时会触发这个事件
  3. 然后initInternale()方法会交给子类具体去实现,
  4. 等待子类处理完成后会把状态更新为INITIALIZED

我们可以进入setStateInternal方法查看最后的关键代码:

代码语言:javascript复制
        // ....
        this.state = state; // 更新状态
        // 根据状态和事件的绑定关系获取对应的事件
        String lifecycleEvent = state.getLifecycleEvent();
        if (lifecycleEvent != null) {
            // 发布对应的事件
            fireLifecycleEvent(lifecycleEvent, data);
        }

  可以看到和对应的事件关联起来了。init方法的逻辑弄清楚后,你会发现start方法,stop方法,destory方法的处理逻辑都是差不多的,可自行观看。而对应的 initInternal()方法的逻辑我们需要在 Server Service Engine Connector等核心组件中再看,这个我们会结合Tomcat的启动流程来带领大家一起查看。下一篇给大家介绍。

0 人点赞