趣操作,Tomcat如何实现一键式启停?( 二 )


从图上你可以看到,组件的生命周期有 NEW、INITIALIZING、INITIALIZED、STARTING_PREP、STARTING、STARTED 等,而一旦组件到达相应的状态就触发相应的事件,比如 NEW 状态表示组件刚刚被实例化;而当 init() 方法被调用时,状态就变成INITIALIZING 状态,这个时候,就会触发 BEFORE_INIT_EVENT 事件,如果有监听器在监听这个事件,它的方法就会被调用 。
重用性:LifeCycleBase抽象基类有了接口,我们就要用类去实现接口 。一般来说实现类不止一个,不同的类在实现接口时往往会有一些相同的逻辑,如果让各个子类都去实现一遍,就会有重复代码 。那子类如何重用这部分逻辑呢?其实就是定义一个基类来实现共同的逻辑,然后让各个子类去继承它,就达到了重用的目的 。
而基类中往往会定义一些抽象方法,所谓的抽象方法就是说基类不会去实现这些方法,而是调用这些方法来实现骨架逻辑 。抽象方法是留给各个子类去实现的,并且子类必须实现,否则无法实例化 。
比如宝马和荣威的底盘和骨架其实是一样的,只是发动机和内饰等配套是不一样的 。底盘和骨架就是基类,宝马和荣威就是子类 。仅仅有底盘和骨架还不是一辆真正意义上的车,只能算是半成品,因此在底盘和骨架上会留出一些安装接口,比如安装发动机的接口、安装座椅的接口,这些就是抽象方法 。宝马或者荣威上安装的发动机和座椅是不一样的,也就是具体子类对抽象方法有不同的实现 。
回到 LifeCycle 接口,Tomcat 定义一个基类 LifeCycleBase 来实现 LifeCycle 接口,把一些公共的逻辑放到基类中去,比如生命状态的转变与维护、生命事件的触发以及监听器的添加和删除等,而子类就负责实现自己的初始化、启动和停止等方法 。为了避免跟基类中的方法同名,我们把具体子类的实现方法改个名字,在后面加上 Internal,叫 initInternal()、startInternal() 等 。我们再来看引入了基类 LifeCycleBase 后的类图:

趣操作,Tomcat如何实现一键式启停?

文章插图
 
从图上可以看到,LifeCycleBase 实现了 LifeCycle 接口中所有的方法,还定义了相应的抽象方法交给具体子类去实现,这是典型的模板设计模式 。
我们还是看一看代码,可以帮你加深理解,下面是 LifeCycleBase 的 init() 方法实现 。
@Overridepublic 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) {...}}这个方法逻辑比较清楚,主要完成了四步:
第一步,检查状态的合法性,比如当前状态必须是 NEW 然后才能进行初始化 。
第二步,触发 INITIALIZING 事件的监听器:
 在这个 setStateInternal 方法里,会调用监听器的业务方法 。
第三步,调用具体子类实现的抽象方法 initInternal() 方法 。我在前面提到过,为了实现一键式启动,具体组件在实现 initInternal() 方法时,又会调用它的子组件的 init() 方法 。
第四步,子组件初始化后,触发 INITIALIZED 事件的监听器,相应监听器的业务方法就会被调用 。
setStateInternal(LifecycleState.INITIALIZED, null, false);总之,LifeCycleBase 调用了抽象方法来实现骨架逻辑 。讲到这里,你可能好奇,LifeCycleBase 负责触发事件,并调用监听器的方法,那是什么时候、谁把监听器注册进来的呢?
分为两种情况:
  • Tomcat 自定义了一些监听器,这些监听器是父组件在创建子组件的过程中注册到子组件的 。比如 MemoryLeakTrackingListener 监听器,用来检测 Context 容器中的内存泄漏,这个监听器是 Host 容器在创建 Context 容器时注册到 Context 中的 。
  • 我们还可以在 server.xml 中定义自己的监听器,Tomcat 在启动时会解析 server.xml,创建监听器并注册到容器组件 。
生周期管理总体类图通过上面的学习,我相信你对 Tomcat 组件的生命周期的管理有了深入的理解,我们再来看一张总体类图继续加深印象 。
趣操作,Tomcat如何实现一键式启停?

文章插图
 
这里请你注意,图中的 StandardServer、StandardService 等是 Server 和 Service 组件的具体实现类,它们都继承了 LifeCycleBase 。
StandardEngine、StandardHost、StandardContext 和 StandardWrapper 是相应容器组件的具体实现类,因为它们都是容器,所以继承了 ContainerBase 抽象基类,而ContainerBase 实现了 Container 接口,也继承了 LifeCycleBase 类,它们的生命周期管理接口和功能接口是分开的,这也符合设计中接口分离的原则 。


推荐阅读