首先我们通过一张简化的类图来回顾一下,从图上你可以看到各种组件的层次关系,图中的虚线表示一个请求在 Tomcat 中流转的过程 。

文章插图
上面这张图描述了组件之间的静态关系,如果想让一个系统能够对外提供服务,我们需要创建、组装并启动这些组件;在服务停止的时候,我们还需要释放资源,销毁这些组件,因此这是一个动态的过程 。也就是说,Tomcat 需要动态地管理这些组件的生命周期 。
在我们实际的工作中,如果你需要设计一个比较大的系统或者框架时,你同样也需要考虑这几个问题:如何统一管理组件的创建、初始化、启动、停止和销毁?如何做到代码逻辑清晰?如何方便地添加或者删除组件?如何做到组件启动和停止不遗漏、不重复?
今天我们就来解决上面的问题,在这之前,先来看看组件之间的关系 。如果你仔细分析过这些组件,可以发现它们具有两层关系 。
- 第一层关系是组件有大有小,大组件管理小组件,比如 Server 管理 Service,Service 又管理连接器和容器 。
- 第二层关系是组件有外有内,外层组件控制内层组件,比如连接器是外层组件,负责对外交流,外层组件调用内层组件完成业务功能 。也就是说,请求的处理过程是由外层组件来驱动的 。
- 第一个原则是先创建子组件,再创建父组件,子组件需要被“注入”到父组件中 。
- 第二个原则是先创建内层组件,再创建外层组件,内层组建需要被“注入”到外层组件 。
为了解决这个问题,我们希望找到一种通用的、统一的方法来管理组件的生命周期,就像汽车“一键启动”那样的效果 。
一键式启停:LifeCycle接口我在前面说到过,设计就是要找到系统的变化点和不变点 。这里的不变点就是每个组件都要经历创建、初始化、启动这几个过程,这些状态以及状态的转化是不变的 。而变化点是每个具体组件的初始化方法,也就是启动方法是不一样的 。
因此,我们把不变点抽象出来成为一个接口,这个接口跟生命周期有关,叫作 LifeCycle 。LifeCycle 接口里应该定义这么几个方法:init()、start()、stop() 和 destroy(),每个具体的组件去实现这些方法 。
理所当然,在父组件的 init() 方法里需要创建子组件并调用子组件的 init() 方法 。同样,在父组件的 start() 方法里也需要调用子组件的 start() 方法,因此调用者可以无差别的调用各组件的 init() 方法和 start() 方法,这就是组合模式的使用,并且只要调用最顶层组件,也就是 Server 组件的 init() 和 start() 方法,整个 Tomcat 就被启动起来了 。下面是LifeCycle 接口的定义 。

文章插图
可扩展性:LifeCycle事件我们再来考虑另一个问题,那就是系统的可扩展性 。因为各个组件 init() 和 start() 方法的具体实现是复杂多变的,比如在 Host 容器的启动方法里需要扫描 webApps 目录下的Web 应用,创建相应的 Context 容器,如果将来需要增加新的逻辑,直接修改 start() 方法?这样会违反开闭原则,那如何解决这个问题呢?开闭原则说的是为了扩展系统的功能,你不能直接修改系统中已有的类,但是你可以定义新的类 。
我们注意到,组件的 init() 和 start() 调用是由它的父组件的状态变化触发的,上层组件的初始化会触发子组件的初始化,上层组件的启动会触发子组件的启动,因此我们把组件的生命周期定义成一个个状态,把状态的转变看作是一个事件 。而事件是有监听器的,在监听器里可以实现一些逻辑,并且监听器也可以方便的添加和删除,这就是典型的观察者模式 。
具体来说就是在 LifeCycle 接口里加入两个方法:添加监听器和删除监听器 。除此之外,我们还需要定义一个 Enum 来表示组件有哪些状态,以及处在什么状态会触发什么样的事件 。因此 LifeCycle 接口和 LifeCycleState 就定义成了下面这样 。

文章插图
推荐阅读
- Win10系统中进入BIOS的详细操作方法
- Mysql写入频繁,怎么破?这是我见过的最清晰的“神操作”
- 又一个骚操作,查询Windows Defender的白名单路径
- CentOS7下重置root密码的操作步骤
- 138条 Vim 命令、操作、快捷键全集
- 怎么才能成为淘宝商家 淘宝开店之后怎么操作
- 开机后不能直达想看的节目,如何为老年人选择一台操作便利的电视
- 网上开网店如何操作 怎样才能开网店成功
- 新手开淘宝店怎么操作 淘宝开店教程新手必学
- Windows操作系统|Win11安卓子系统轻松安装Play商店?小心中木马
