🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### Lifecycle生命周期 前面我们看到`Container`容器 继承了`Lifecycle`生命周期。如果想让一个系统能够对外提供服务,我们需要创建、组装并启动这些组件;在服务停止的时候,我们还需要释放资源,销毁这些组件,因此这是一个动态的过程。也就是说,Tomcat 需要动态地管理这些组件的生命周期。 如何统一管理组件的创建、初始化、启动、停止和销毁?如何做到代码逻辑清晰?如何方便地添加或者删除组件?如何做到组件启动和停止不遗漏、不重复? ##### 一键式启停:LifeCycle 接口 设计就是要找到系统的变化点和不变点。这里的不变点就是每个组件都要经历创建、初始化、启动这几个过程,这些状态以及状态的转化是不变的。而变化点是每个具体组件的初始化方法,也就是启动方法是不一样的。 因此,Tomcat 把不变点抽象出来成为一个接口,这个接口跟生命周期有关,叫作 LifeCycle。LifeCycle 接口里定义这么几个方法:`init()、start()、stop() 和 destroy()`,每个具体的组件(也就是容器)去实现这些方法。 在父组件的`init()`方法里需要创建子组件并调用子组件的`init()`方法。同样,在父组件的`start()`方法里也需要调用子组件的`start()`方法,因此调用者可以无差别的调用各组件的`init()`方法和`start()`方法,这就是**组合模式**的使用,并且只要调用最顶层组件,也就是 Server 组件的`init()`和`start()`方法,整个 Tomcat 就被启动起来了。所以 Tomcat 采取组合模式管理容器,容器继承 LifeCycle 接口,这样就可以向针对单个对象一样一键管理各个容器的生命周期,整个 Tomcat 就启动起来。 ##### 可扩展性:LifeCycle 事件 我们再来考虑另一个问题,那就是系统的可扩展性。因为各个组件`init()`和`start()`方法的具体实现是复杂多变的,比如在 Host 容器的启动方法里需要扫描 webapps 目录下的 Web 应用,创建相应的 Context 容器,如果将来需要增加新的逻辑,直接修改`start()`方法?这样会违反开闭原则,那如何解决这个问题呢?开闭原则说的是为了扩展系统的功能,你不能直接修改系统中已有的类,但是你可以定义新的类。 **组件的`init()`和`start()`调用是由它的父组件的状态变化触发的,上层组件的初始化会触发子组件的初始化,上层组件的启动会触发子组件的启动,因此我们把组件的生命周期定义成一个个状态,把状态的转变看作是一个事件。而事件是有监听器的,在监听器里可以实现一些逻辑,并且监听器也可以方便的添加和删除**,这就是典型的**观察者模式**。 以下就是`Lyfecycle`接口的定义: ![](https://img.kancloud.cn/ca/58/ca584b5a8df24e41c194d11fd24bd364_347x291.png) ##### 重用性:LifeCycleBase 抽象基类 再次看到抽象模板设计模式。 有了接口,我们就要用类去实现接口。一般来说实现类不止一个,不同的类在实现接口时往往会有一些相同的逻辑,如果让各个子类都去实现一遍,就会有重复代码。那子类如何重用这部分逻辑呢?其实就是定义一个基类来实现共同的逻辑,然后让各个子类去继承它,就达到了重用的目的。 Tomcat 定义一个基类 LifeCycleBase 来实现 LifeCycle 接口,把一些公共的逻辑放到基类中去,比如生命状态的转变与维护、生命事件的触发以及监听器的添加和删除等,而子类就负责实现自己的初始化、启动和停止等方法。 ``` public abstract class LifecycleBase implements Lifecycle{ // 持有所有的观察者 private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>(); /** * 发布事件 * * @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); } } // 模板方法定义整个启动流程,启动所有容器 @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); } } } ``` Tomcat 为了实现一键式启停以及优雅的生命周期管理,并考虑到了可扩展性和可重用性,将面向对象思想和设计模式发挥到了极致,`Containaer`接口维护了容器的父子关系,`Lifecycle`组合模式实现组件的生命周期维护,生命周期每个组件有变与不变的点,运用模板方法模式。 分别运用了**组合模式、观察者模式、骨架抽象类和模板方法**。 如果你需要维护一堆具有父子关系的实体,可以考虑使用组合模式。 观察者模式听起来 “高大上”,其实就是当一个事件发生后,需要执行一连串更新操作。实现了低耦合、非侵入式的通知与更新机制 ![](https://img.kancloud.cn/2c/16/2c169a2f1103d1a5f1d712934d24f0eb_958x681.png) `Container`继承了 LifeCycle,StandardEngine、StandardHost、StandardContext 和 StandardWrapper 是相应容器组件的具体实现类,因为它们都是容器,所以继承了 ContainerBase 抽象基类,而 ContainerBase 实现了 Container 接口,也继承了 LifeCycleBase 类,它们的生命周期管理接口和功能接口是分开的,这也符合设计中**接口分离的原则**