虚线表示一个请求在Tomcat中的流转
若想让一个系统能对外提供服务,需创建、组装并启动这些组件;在服务停止时,还需要释放资源,这是一个动态过程。即Tomcat需动态管理这些组件的生命周期。
当我们设计一个较大系统或框架时,也需要考虑:
组件有大有小,大组件管理小组件,比如Server管理Service,Service又管理连接器和容器。 组件有外有内,外层组件控制内层组件,比如连接器是外层组件,负责对外交流,外层组件调用内层组件完成业务功能。即请求的处理过程由外层组件驱动。
这两层关系决定了系统在创建组件时应该遵循一定的顺序:
因此,最直观的做法就是将图上所有的组件按照先小后大、先内后外的顺序创建,然后组装。 这个思路其实很有问题:
为了解决这个问题,我们希望找到一种通用的、统一的方法来管理组件的生命周期,就像汽车“一键启动”那样的效果。
设计就是要找到系统的变化点和不变点:
把不变点抽象出来成为一个接口,这个接口跟生命周期有关,叫Lifecycle。Lifecycle接口里应该定义这么几个方法:init、start、stop和destroy,每个具体的组件去实现这些方法。
父组件的init方法里需要创建子组件并调用子组件的init方法。同理父组件的start调用子组件的start,因此调用者可以无差调用各组件的init、start,这就是组合模式,并且只要调用最顶层组件,也就是Server组件的init和start方法,整个Tomcat就被启动起来了。
在Host容器的启动方法需扫描webapps目录下的Web应用,创建相应Context容器,若以后新增逻辑,直接修改start方法?这会违反开闭原则,那如何解决这个问题呢?
组件的init和start调用是由它的父组件的状态变化触发的,上层组件的初始化会触发子组件的初始化,上层组件的启动会触发子组件的启动,因此我们把组件的生命周期定义成一个个状态,把状态的转变看作是一个事件。 而事件有监听器,在监听器里可以实现一些逻辑。
于是可以在Lifecycle接口里加入两个方法:添加监听器和删除监听器。还需要定义一个Enum表示组件有哪些状态,以及处在什么状态会触发什么样的事件。 因此Lifecycle接口和LifecycleState就定义成了:
组件的生命周期有NEW、INITIALIZING、INITIALIZED、STARTING_PREP、STARTING、STARTED等,一旦组件到达相应的状态就触发相应的事件,比如
有了接口,就要用类去实现接口。不同类在实现接口时往往会有一些相同的逻辑,如果让各个子类都去实现一遍,就会有重复代码。那子类如何重用这部分逻辑呢? 定义基类实现共同逻辑,然后让各个子类去继承它,达到重用。
而基类中往往会定义一些抽象方法,基类不会去实现这些方法,而是调用这些方法来实现骨架逻辑。留给子类实现,并且子类必须实现,否则无法实例化。
Tomcat定义一个基类LifecycleBase来实现Lifecycle接口,把一些公共逻辑放到该基类,比如
子类就负责实现自己的初始化、启动和停止等方法。为了避免跟基类中的方法同名,我们把具体子类的实现方法改个名字,在后面加上Internal,叫initInternal、startInternal等。
LifecycleBase定义了相应的抽象方法交给具体子类去实现,这是模板设计模式。
@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) {
...
}
}
这个方法逻辑比较清楚,主要完成了四步:
LifecycleBase负责触发事件,并调用监听器的方法,那是什么时候、谁把监听器注册进来的呢?分为两种情况:
StandardServer、StandardService等是Server和Service组件的具体实现类,它们都继承了LifecycleBase。
StandardEngine、StandardHost、StandardContext和StandardWrapper是相应容器组件的具体实现类,因为它们都是容器,所以继承了ContainerBase抽象基类,而ContainerBase实现了Container接口,也继承了LifecycleBase类,它们的生命周期管理接口和功能接口是分开的,这也符合设计中接口分离的原则。