深入浅出Tomcat/3 - Tomcat生命周期
在上面的部分,其实我们已经接触到Tomcat的生命周期了,接下来我们将仔细讨论和学习Tomcat的生命周期的具体实现。
LifeCycle接口
这个LifeCycle接口上面在讲解Server和Service时其实已经有所接触。Tomcat通过org.apache.catalina.LifeCycle接口统一接管Tomcat的生命周期,所有继承自它的组件(即Component)都需要实现这个接口。
我们先俯视看一看LifeCycle接口的定义。
根据上图,我们清晰看到上图包含了4个生命周期的方法:
1. init
2. start
3. stop
4. destroy
这4个方法不用多解释,很直观,我们前面也有所接触。
同时,每个方法又定义了几个常量字符串,用于LifeCycleEvent事件的type属性,用来描述各个状态的变化。举个例子,对于init周期,它包含before_init和 after_init两个字符串,其他类似。
它同时也定义了三个管理监听器的方法,分别如下:
• addLifeCycleListener
• findLifeCycleListeners
• removeLifeCycleListener
显而易见,它们用来增加、查找和删除生命周期的监听器。
最后定义了获取当前状态信息,用2个方法实现:
• getState
• getStateName
getState返回的是LifeCycleState,而且LifeCycleState是一个枚举类型。如下
public enum LifecycleState {
NEW(false, null),
INITIALIZING(false, Lifecycle.BEFORE_INIT_EVENT),
INITIALIZED(false, Lifecycle.AFTER_INIT_EVENT),
STARTING_PREP(false, Lifecycle.BEFORE_START_EVENT),
STARTING(true, Lifecycle.START_EVENT),
STARTED(true, Lifecycle.AFTER_START_EVENT),
STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT),
STOPPING(false, Lifecycle.STOP_EVENT),
STOPPED(false, Lifecycle.AFTER_STOP_EVENT),
DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT),
DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT),
FAILED(false, null);
我们看到它列举了Tomcat各个生命周期。
LifeCycleBase类
LifeCycleBase是实现了LifeCycle的抽象类,Tomcat的几个生命周期的管理都交给它,所以弄清楚这个类的实现,基本上也就明白了Tomcat的生命周期了。
在LifeCycle接口里,我们提到的4个周期的方法以及3个监听器的管理方法,最后还有2个获取状态信息的方法,我们接下来逐一分析和了解。
4个生命周期方法
方法init
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
} try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.initFail", toString());
}
}
先判断当前状态是不是NEW,如果不是抛出异常。因为init时,代表的是Tomcat的初始化,所以其状态需要为NEW的。接下来就是设置状态为LifecycleState.INITIALIZING,也就是正在初始化,然后调用initInternal开始初始化了,初始化完之后设置为LifecycleState.INITIALIZED
这里需要多说一下invalidTransition这个方法,因为后面很多地方都会用到它。
private void invalidTransition(String type) throws LifecycleException {
String msg = sm.getString("lifecycleBase.invalidTransition", type, toString(), state);
throw new LifecycleException(msg);
}
我们看到invalidTransition方法其实没做其他事情,就是抛出了一个异常而已。
同时,也介绍一下setStateInternal这个方法,后面也用的较多。
private synchronized void setStateInternal(LifecycleState state, Object data, boolean check)
throws LifecycleException { if (log.isDebugEnabled()) {
log.debug(sm.getString("lifecycleBase.setState", this, state));
} if (check) {
// Must have been triggered by one of the abstract methods (assume
// code in this class is correct)
// null is never a valid state
if (state == null) {
invalidTransition("null");
// Unreachable code - here to stop eclipse complaining about
// a possible NPE further down the method
return;
} // Any method can transition to failed
// startInternal() permits STARTING_PREP to STARTING
// stopInternal() permits STOPPING_PREP to STOPPING and FAILED to
// STOPPING
if (!(state == LifecycleState.FAILED ||
(this.state == LifecycleState.STARTING_PREP &&
state == LifecycleState.STARTING) ||
(this.state == LifecycleState.STOPPING_PREP &&
state == LifecycleState.STOPPING) ||
(this.state == LifecycleState.FAILED &&
state == LifecycleState.STOPPING))) {
// No other transition permitted
invalidTransition(state.name());
}
} this.state = state;
String lifecycleEvent = state.getLifecycleEvent();
if (lifecycleEvent != null) {
fireLifecycleEvent(lifecycleEvent, data);
}
}
变量check是传进来的,是否要检查。至于后面的逻辑注释写的比较清楚,不符合逻辑的状态转化都会报错,不多解释。
后面的fireLifeCycleEvent的代码如下:
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);
}
}
也是很简单,就是让LifeCycle的事件监听者触发事件,至于如何去使用这些event,则是每个监听者自己的业务逻辑了。这也符合一贯的代码实现方法,如果有多个监听器,然后逐一触发这些监听器,这其实是事件监听最基本的实现方法。
以上就是init的方法实现。
方法start
直接上代码
public final synchronized void start() throws LifecycleException { if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) { if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
} return;
} if (state.equals(LifecycleState.NEW)) {
init();
} else if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
} try {
setStateInternal(LifecycleState.STARTING_PREP, null, false);
startInternal();
if (state.equals(LifecycleState.FAILED)) {
// This is a 'controlled' failure. The component put itself into the
// FAILED state so call stop() to complete the clean-up.
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
// This is an 'uncontrolled' failure so put the component into the
// FAILED state and throw an exception.
handleSubClassException(t, "lifecycleBase.startFail", toString());
}
}
第一步就是检查状态的合理性,如果已经在准备或者开始了,直接抛出已经开始的exception。如果是NEW的话,说明init这一步没有做,那就初始化一下。
正式开始启动了,首先需要将状态设置为LifecycleState.STARTING_PREP,接下调用startInternal开始启动。执行完startInternal后验证state,如果状态不对,要么停止,要么抛出异常。
方法destroy
destroy其实调用的是stop方法。
方法stop
Stop方法
public final synchronized void stop() throws LifecycleException { if (LifecycleState.STOPPING_PREP.equals(state) || LifecycleState.STOPPING.equals(state) ||
LifecycleState.STOPPED.equals(state)) { if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStopped", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStopped", toString()));
} return;
} if (state.equals(LifecycleState.NEW)) {
state = LifecycleState.STOPPED;
return;
} if (!state.equals(LifecycleState.STARTED) && !state.equals(LifecycleState.FAILED)) {
invalidTransition(Lifecycle.BEFORE_STOP_EVENT);
} try {
if (state.equals(LifecycleState.FAILED)) {
// Don't transition to STOPPING_PREP as that would briefly mark the
// component as available but do ensure the BEFORE_STOP_EVENT is
// fired
fireLifecycleEvent(BEFORE_STOP_EVENT, null);
} else {
setStateInternal(LifecycleState.STOPPING_PREP, null, false);
} stopInternal(); // Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
if (!state.equals(LifecycleState.STOPPING) && !state.equals(LifecycleState.FAILED)) {
invalidTransition(Lifecycle.AFTER_STOP_EVENT);
} setStateInternal(LifecycleState.STOPPED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.stopFail", toString());
} finally {
if (this instanceof Lifecycle.SingleUse) {
// Complete stop process first
setStateInternal(LifecycleState.STOPPED, null, false);
destroy();
}
}
}
首先,检查一下当前状态,如果当前状态为与STOP相关的几个状态,则抛出已经停止的异常。如果为NEW,说明还没有初始化,直接将STOPPED的状态赋值一下即可。如果状态不是STARTED,是不可以停止的,这个时候直接抛出异常。将触发的event给监听器和前面类似,不多做解释。
3个关于监听器的方法
前面介绍到LifeCycleBase有三个方法来管理监听器:
• addLifeCycleListener
• findLifeCycleListeners
• removeLifeCycleListener
我们看看它们的实现。
@Override
public void addLifecycleListener(LifecycleListener listener) {
lifecycleListeners.add(listener);
} /**
* {@inheritDoc}
*/
@Override
public LifecycleListener[] findLifecycleListeners() {
return lifecycleListeners.toArray(new LifecycleListener[0]);
} /**
* {@inheritDoc}
*/
@Override
public void removeLifecycleListener(LifecycleListener listener) {
lifecycleListeners.remove(listener);
}
代码比较简单了,但是需要注意的是lifecycleListeners是一个CopyOnWriteArrayList。
private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();
关于CopyOnWriteArrayList,它继承了ArrayList。我们都知道ArrayList不是线程安全的,但CopyOnWriteArrayList则是线程安全的。
/**
* Creates a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection of initially held elements
* @throws NullPointerException if the specified collection is null
*/
public CopyOnWriteArrayList(Collection<? extends E> c) {
Object[] elements;
if (c.getClass() == CopyOnWriteArrayList.class)
elements = ((CopyOnWriteArrayList<?>)c).getArray();
else {
elements = c.toArray();
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elements.getClass() != Object[].class)
elements = Arrays.copyOf(elements, elements.length, Object[].class);
}
setArray(elements);
}
我们可以看到代码行
elements = c.toArray();
它其实是Copy一份c,这种开销是很大的。尽管开销很大,但是当遍历操作的数量大大超过可变操作的数量时,这种方法可能比其他替代方法更有效。
同时,在add和remove时,我们看到使用到ReentrantLock来保证线程安全。
CopyOnWriteArray比较适用于读多修改少的情景。在Tomcat这里,一般来说Listener都是在Server.xml,如果在想增加或删除Listener,必须重新启动Tomcat,在这种场景下,其实正好符合读多写少这种特征。
public void add(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
Object[] newElements;
int numMoved = len - index;
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + 1);
else {
newElements = new Object[len + 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
newElements[index] = element;
setArray(newElements);
} finally {
lock.unlock();
}
} /**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices). Returns the element that was removed from the list.
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
E oldValue = get(elements, index);
int numMoved = len - index - 1;
if (numMoved == 0)
setArray(Arrays.copyOf(elements, len - 1));
else {
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
setArray(newElements);
}
return oldValue;
} finally {
lock.unlock();
}
}
获取状态
@Override
public LifecycleState getState() {
return state;
} /**
* {@inheritDoc}
*/
@Override
public String getStateName() {
return getState().toString();
}
代码非常简单,不介绍了。
深入浅出Tomcat/3 - Tomcat生命周期的更多相关文章
- how tomcat works 六 生命周期
我觉得这一章叫tomcat中的观察者模式,比较好! 首先,不要看本章,请查看一下关于观察者模式的资料比较好. 推荐以下知识点 基础篇 设计模式之禅----观察者模式 大家可以找到pdf阅读一下 另外本 ...
- Tomcat 8(九)解读Tomcat组件的生命周期(Lifecycle)
Tomcat 8(七)解读Bootstrap介绍过.运行startup.bat.将引发Tomcat一连串组件的启动.事实上这一连串启动是通过组件的生命周期(Lifecycle)实现的 今天来看看Lif ...
- 【转】Tomcat组件生命周期管理
Tomcat组件生命周期管理 Tomcat中Server,Service,Connector,Engine,Host,Context,它们都实现了org.apache.catalina.Lifecyc ...
- Tomcat 源码分析(一)——启动与生命周期组件
写在前面的话:读Tomcat源码也有段时间了,大领悟谈不上.一些小心得记录下来,供大家参考相护学习. 一.启动流程 Tomcat启动首先需要熟悉的是它的启动流程.和初学者第一天开始写Hello Wor ...
- tomcat生命周期的管理——生命周期统一接口Lifecycle
我们知道Tomcat的架构设计是清晰的.模块化的,其拥有很多组件,假如我们要启动Tomcat,可以一个一个启动组件,但这样启动有很多缺点,不仅麻烦,而且容易漏了组件启动,还会对后面动态组件扩展带来麻烦 ...
- Tomcat中组件的生命周期管理公共接口Lifecycle
Tomcat的组件都会实现一个Lifecycle接口,以方便组件的生命周期的统一管理 interface Lifecycle 组件生命周期中主要的几个方法 增加监听器,事件委托机制 public vo ...
- TOMCAT源码分析——生命周期管理
前言 从server.xml文件解析出来的各个对象都是容器,比如:Server.Service.Connector等.这些容器都具有新建.初始化完成.启动.停止.失败.销毁等状态.tomcat的实现提 ...
- Tomcat生命周期
来源 本文整理自 <Tomcat内核设计剖析>.<Tomcat结构解析> Lifecycle接口 Lifecycle接口统一管理Tomcat生命周期.一共做了4件事: 定义 ...
- Tomcat源码分析 (三)----- 生命周期机制 Lifecycle
Tomcat里面有各种各样的组件,每个组件各司其职,组件之间又相互协作共同完成web服务器这样的工程.在这些组件之上,Lifecycle(生命周期机制)至关重要!在学习各个组件之前,我们需要看看Lif ...
随机推荐
- Windows上通过bat调用jmx进行循环运行
1.jmx测试脚本中有两个线程组: 1)第一个线程组:模拟60台客户机并发像服务器发送上报请求,需要调用线程组的循环运行 2)第二个线程组:60台客户机上线后,模拟管理平台对客户机进行基础操作,如:创 ...
- 章节三、6-Getters-Setters和this关键字part02
一.如何在一个类中创建另外一个类的对象,然后用这个类的对象的引用来访问这个对象里面的成员,如下: //如何在一个类中创建另外一个类的对象,然后用这个类的对象的引用来访问这个对象里面的成员 Car bm ...
- java导出数据到excel里:直接导出和导出数据库数据
一.直接导出 package com.ij34.util; import java.io.FileNotFoundException; import java.io.FileOutputStream; ...
- NVIDIA显卡笔记本安装ubuntu驱动以及分辨率之详解
随着对ubuntu的了解,突然想在自己的笔记本上装一个双系统.在网上查了安装方法之后,发现因为nvidia显卡的原因会出现一些问题,结果在我自己装了之后发现问题要比看到的多,再看了无数个帖子之后,最终 ...
- 洗礼灵魂,修炼python(63)--爬虫篇—re模块/正则表达式(1)
爬虫篇前面的某一章了,我们要爬取网站页面源代码的数据,要从中获取到我们想要的数据,是不是感觉很费力,确实费力对吧?那么有没有什么有利的工具来解决这个问题呢?那就是这一篇博文的主题—— 正则表达式简介 ...
- 在Unity中对Lua进行调试
前言 接我之前的文章,讲到使用IntelliJ IDEA(做为Lua的编辑器)+EmmlyLua(插件),当然EmmlyLua也提供调试功能的. Lua代码提示和方法跳转 在Lua中提示UnityEn ...
- 设置Linux环境变量的方法和区别_Ubuntu
设置 Linux 环境变量可以通过 export 实现,也可以通过修改几个文件来实现,有必要弄清楚这两种方法以及这几个文件的区别. 通过文件设置 Linux 环境变量 首先是设置全局环境变量,对所有用 ...
- GitHub-暂存区与版本回退
参考博文:廖雪峰Git教程 1. 工作区和暂存区 Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念. 1.1. 工作区(Working Directory) 就是你在电脑里能看到的目录 ...
- Docker: docker image常用命令实战
#docker列出镜像[root@192 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEnginx latest 881bd08c0b08 ...
- C语言的main函数到底该怎么写
公众号[编程珠玑]:专注但不限于分享计算机编程基础,Linux,C语言,C++,Python,数据库等编程相关[原创]技术文章,号内包含大量经典电子书和视频学习资源.欢迎一起交流学习,一起修炼计算机“ ...