tomcat + spring mvc 原理(一):tomcat原理综述比较详细的叙述了tomcat容器的静态结构和容器的配置,从tomcat动态运行来讲,我也在原理(一)中简单论述了:
显然,这种运作模式要求:tomcat需要有监视指定目录,一旦有新的war包加入,就完成解包并动态加载编译后的class的能力;tomcat需要有网络端口开闭和监视的机制,维护线程池来处理随时可能到来的请求;tomcat还需要将到来的请求顺利地传递给spring mvc服务,然后再将服务返回的数据发送出去。
其实,对tomcat的理解,不只是应该了解静态容器结构,对于动态运行方面,应该包括:
- 容器的初始加载和启动
- war包动态监视和加载
- 网络请求的监控和处理
这样基本就涵盖了tomcat所有的基本运作原理。本文主要涉及第一部分:容器的初始加载与启动。
容器通用生命周期标准
tomcat的容器,包括Server、Service、Connector、Engine、Host、Context、Wrapper都继承自同一个管理生命周期的接口:Lifecycle。
首先,这个接口类定义了这些容器的初始化、启动、停止和销毁的标准生命周期方法。
public interface Lifecycle {
......
//容器初始化
public void init() throws LifecycleException;
//容器启动
public void start() throws LifecycleException;
//容器停止
public void stop() throws LifecycleException;
//容器销毁
public void destroy() throws LifecycleException;
......
}
其次,这个接口类定义了这些容器所处生命周期的状态事件名和状态事件监听器的管理。
public interface Lifecycle {
......
/**
*13种生命周期状态事件的名字。通过名字还是比较比较好理解的
**/
public static final String BEFORE_INIT_EVENT = "before_init";
public static final String AFTER_INIT_EVENT = "after_init";
public static final String START_EVENT = "start";
public static final String BEFORE_START_EVENT = "before_start";
public static final String AFTER_START_EVENT = "after_start";
public static final String STOP_EVENT = "stop";
public static final String BEFORE_STOP_EVENT = "before_stop";
public static final String AFTER_STOP_EVENT = "after_stop";
public static final String AFTER_DESTROY_EVENT = "after_destroy";
public static final String BEFORE_DESTROY_EVENT = "before_destroy";
public static final String PERIODIC_EVENT = "periodic";
public static final String CONFIGURE_START_EVENT = "configure_start";
public static final String CONFIGURE_STOP_EVENT = "configure_stop";
/**
*两个获取生命周期状态的方法
**/
//获取状态,内含事件和状态名
public LifecycleState getState();
//获取状态名
public String getStateName();
/**
*状态事件监听器。LifecycleListener中包含一个处理事件的方
*法LifecycleEvent()
**/
//添加状态事件监听器
public void addLifecycleListener(LifecycleListener listener);
//获取所有状态事件监听器
public LifecycleListener[] findLifecycleListeners();
//移除状态事件监听器
public void removeLifecycleListener(LifecycleListener listener);
......
}
容器通用生命周期的实现
Lifecycle接口类的通用实现是在LifecycleBase类中。
容器都继承自LifeCycleBase类,LifecycleBase类为容器提供了基本生命周期方法的一般实现和生命周期状态监听器的管理实现。
-
生命周期状态监听器的管理实现
这一部分主要是监听器管理的实现,但真正发挥作用是在生命周期方法的实现中。
LifecycleBase中使用一个自己定义的List--CopyOnWriteArrayList来存储LifecycleListener,本质上还是一个List,具体实现过于细节,这里不做研究。同时LifecycleBase基于监听器的List,对添加监听器、移除监听器、获取所有监听器的方法实现了逻辑。
private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();
//添加状态事件监听器的方法实现
@Override
public void addLifecycleListener(LifecycleListener listener) {
lifecycleListeners.add(listener);
}
//获取所有状态事件监听器的方法实现
@Override
public LifecycleListener[] findLifecycleListeners() {
return lifecycleListeners.toArray(new LifecycleListener[0]);
}
//移除状态事件监听器的方法实现
@Override
public void removeLifecycleListener(LifecycleListener listener) {
lifecycleListeners.remove(listener);
}
需要特别注意的是,LifecycleBase实现了生命周期状态事件监听器处理事件的方法,其实就是遍历了监听器List,使用监听器的lifecleEvent方法处理了事件。这一方法在后面实现的init(),start()等生命周期的方法中都有间接调用,相当于只要更改了容器所处生命周期的状态,就需要对发布这一事件,调用该容器的关注该事件的监听器处理事件。
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);
}
}
-
生命周期方法实现
LifecycleBase实现了Lifecycle的标准生命周期方法init()、start()、stop()、destroy()的逻辑。下面主要以init()和start()为例介绍实现的基本特点。
//init()生命周期方法实现
@Override
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
//new状态是最初加载时必须为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());
}
}
通过init()的具体代码可以发现,LifecycleBase中主要就是设置了生命周期的状态,其中setStateInternal()除了设置了状态、校验了状态是否正确之外,还调用了上文提到的fireLifecycleEvent()响应了当前状态事件,触发了监听该事件的监听器监听到该事件的逻辑。由于不同子类(主要是不同容器)在初始化时所需要做的事不一样,所以initInternal()是真正被子类重载后做初始化的事情的方法。
start()内部逻辑的特点和init()很相似,也是做了设置生命周期的状态,稍微复杂一些是需要:1.判断状态是否处于可以start()的阶段,不然就需要先init();2.是否失败需要stop()。
@Override
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)) {
stop(); //失败直接终止
} else if (!state.equals(LifecycleState.STARTING)) {
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.startFail", toString());
}
}
可以看到,依旧有很多状态变更,和处理状态变更、发布状态变更事件的setStateInternal()的调用。
宏观来看各种容器生命周期的实际流程
在原理(一)我已经详细介绍了各种容器的配置、静态嵌套结构和各自的功能,铺垫了这么多,终于可以介绍各种容器是如果加载初始化的了。
首先按照基本嵌套结构,如图,最开始需要先启动Server,不管是Tomcat独立部署包还是spring mvc中的嵌入式tomcat-embed包,都需要先调用Server.init(),然后调用Server.start()。实际上,tomcat中Server的标准实现是StandardServer,根据LifecycleBase中实现的init()和start()的代码,实际上会调用子类重载的initInternal()和startInternal()方法。在StandardServer的initInternal()和startInternal()方法中,会调用StandardServer类中的Service实例的init()和start()方法.....另一个方面,在StandardServer的addService()方法也会调用添加的Service实例(实际是StandardService类)的start()方法。同样的逻辑适用于Service下的Engine容器。
Engine以下的Host、Context、Wrapper略有不同,因为它们都实现自Container接口,继承了Container接口的标准实现ContainerBase。Container中将每一种容器的包含关系定义为父子关系,即Host是Engine类的Container child,使用这个Container[]来存储所有Host子容器。同理Host和Context、Context和Wrapper都是相同的关系。在ContainerBase中实现的startInternal()有:
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
}
需要特别注意的是,Container启动子容器的时候不一定是通过init()或者start()中调用相应子容器的生命周期函数。在容器的addChild方法中,也会调用子容器的start()方法,初始化加载和启动子容器,比如host的addChild(context)方法会调用context的start方法。
统一使用多线程执行调用child的start方法,这样各个容器都能按顺序初始化和启动。
关于边界的情况,比如Wrapper部分,由于和spring mvc相关,会在spring mvc原理里面叙述。
本系列文章:
tomcat + spring mvc原理(一):tomcat原理综述和静态架构
tomcat + spring mvc原理(二):tomcat容器初始化加载和启动
tomcat + spring mvc原理(三):tomcat网络请求的监控与处理1
tomcat + spring mvc原理(四):tomcat网络请求的监控与处理2
tomcat + spring mvc原理(五):tomcat Filter组件实现原理
tomcat + spring mvc原理(六):tomcat WAR包的部署与加载
tomcat + spring mvc原理(七):spring mvc的Servlet和九大标准组件的静态结构与初始化
tomcat + spring mvc原理(八):spring mvc对请求的处理流程