Tomcat中组件将其抽象为接口Lifecycle,通过它控制组件的生命周期,Lifecycle是组件的生命线。组件只要继承这个接口并实现其中的方法就可以统一被拥有它的组件控制了,这样一层一层的直到一个最高级的组件就可以控制Tomcat中所有组件的生命周期,这个最高的组件就是Server,而控制Server的是Startup,也就是启动和关闭Tomcat。
catalina允许一个组件包含多个其他组件,当父组件启动和关闭的时候, 会依次遍历注册到该容器的子容器,调用子容器的start/stop方法,像链式爆炸一样,把整个关联的容器都启动/关闭
Bootstrape类为Tomcat的入口,所有的组件够通过实现Lifecycle接口来管理生命周期,Tomcat启动的时候只需调用Server容器的start(),然后父容器依序启动他所包含的子容器,关闭也是如此。
Tomcat的架构设计是清晰的,模块化的,它有很多组件,假如在启动Tomcat时一个一个组件启动,这不仅麻烦而且容易遗漏组件,还会对后面的动态组件扩展带来麻烦。对于这个问题,Tomcat的设计者提供了一个解决方案:用Lifecycle管理启动、停止、关闭。
Tomcat从初始化到结束,期间必定会经历很多其他的状态,每一个状态都标志着Tomcat现在处于什么阶段。
* start()* -----------------------------* | |* | init() |* NEW -»-- INITIALIZING |* | | | | ------------------«-----------------------* | | |auto | | |* | | \|/ start() \|/ \|/ auto auto stop() |* | | INITIALIZED --»-- STARTING_PREP --»- STARTING --»- STARTED --»--- |* | | | | |* | |destroy()| | |* | --»-----«-- ------------------------«-------------------------------- ^* | | | |* | | \|/ auto auto start() |* | | STOPPING_PREP ----»---- STOPPING ------»----- STOPPED -----»-----* | \|/ ^ | ^* | | stop() | | |* | | -------------------------- | |* | | | | |* | | | destroy() destroy() | |* | | FAILED ----»------ DESTROYING ---«----------------- |* | | ^ | |* | | destroy() | |auto |* | --------»----------------- \|/ |* | DESTROYED |* | |* | stop() |* ----»-----------------------------»------------------------------复制代码
例如一个容器调用init后,状态的转化是NEW->INITIALIZING->INITIALIZED,其中从INITIALIZING到INITIALIZED时自动变化的,并不需要人为操作。接着调用start(),状态则变化为INITIALIZED->STARTING_PREP->STARTING->STARTED,这个过程全部自动完成。接下来,如果调用stop(),状态变化就为STARTED->STOPPING_PREP->STOPPING->STOPPED。如果在生命周期的某个阶段发生意外,则可能经历xx->DESTROYING->DESTROYED
Lifecycle四个生命周期阶段:init(初始化)、start(启动)、stop(停止)、destroy(销毁)
另外,事件的触发也是通过这些状态来进行判定的。
类图链接:
事件Listener会预先调用容器的addLifecycleListener方法,将事件监听器加到该容器维护的监听器数组中,于是容器在特定的时机调用 lifecycle.fireLifecycleEvent(START_EVENT, null);就会遍历这些监听器,调用这些监听器的lifecycleEvent()方法,这是一种典型的发布者订阅者模式的应用
Tomcat 各类容器的配置过程就是通过添加 listener 的方式来进行的,从而达到配置逻辑与容器的解耦。
- EngineConfig:主要打印启动和停止日志
- HostConfig:主要处理部署应用,解析应用 META-INF/context.xml 并创建应用的Context
- ContextConfig:主要解析并合并 web.xml,扫描应用的各类 web 资源 (filter、servlet、listener)
MapperListener组件:StandService包含MapperListener组件,它继承LifecycleMBeanBase,它启动会被注册到jmx上面去。它实现了ContainerListener和LifecycleListener,既可以响应container的事件,例如添加child和valve,同时还可以响应组件的状态事件。
Java事件处理模型
事件源(Event Source):即事件发生的场所,就是指各个组件,如按钮等,点击按钮其实就是组件上发生的一个事件
事件(Event):事件封装了组件上发生的事情,比如按钮单击、按钮松开等等
事件监听器(Event Listener):负责监听事件源上发生的特定类型的事件,当事件到来时还必须负责处理相应的事件
观察者模式
Tomcat组件的用LifecycleListener对生命周期Lifecycle和容器进行监听,各个组件在其生命期中会有各种行为,而这些行为都会触发相应的事件,Tomcat就是通过侦听这些事件达到对这些行为进行扩展的目的。在看组件的init和start过程中会看到大量如:fireLifecycleEvent(CONFIGURE_START_EVENT, null);这样的代码,这就是对某一类型事件的触发,如果你想在其中加入自己的行为,就只用注册相应类型的事件即可。
模板方法模式
把公共的动作抽象到父类中,在父类(LifecycleBase)的方法中调用一些抽象方法,而这些抽象方法留给子类去实现,从而完成公共动作和特殊动作的分离。
红色标注的四个方法就是模板方法模式中的钩子方法,子类可以通过实现钩子方法来纳入到基类已经流程化好的生命周期管理中。例如LifecycleBase类中init和start方法,其中的initInternal和startInternal方法是抽象方法,所有容器都直接或间接继承了LifecycleBase,在初始化和启动时被每个容器会调用其init和start方法,这些抽象方法都是在子类中实现的。每个组件的启动都会调用父类的start()方法,而start()方法又会调用本类的startInternal()方法,stop方法类似。在父类的start()方法定义了一些组件共性的动作,而在startInternal()方法中定义了组件自己的特殊动作。并且每个组件都可以自行添加自己的监听器。从运行的代码可以看到,只要设置好每个组件的关系就可以统一管理每个组件的启动关闭了。Catalina中的生命周期管理的模式大概就是这样。