一般都是通过bat或者sh脚本启动的,这些脚本都是调用的Bootstrap类的main方法,所以先分析Bootstrap。
它的主要作用就是接收脚本的参数,然后创建一个Catalina对象,把操作传递给Catalina对象。
// org/apache/catalina/startup/Bootstrap.java
private static Bootstrap daemon = null;
private Object catalinaDaemon = null;
protected ClassLoader commonLoader = null;
protected ClassLoader catalinaLoader = null;
protected ClassLoader sharedLoader = null;
public static void main(String args[]) {
Bootstrap bootstrap = new Bootstrap();
// 初始化 创建commonLoader,catalinaLoader,sharedLoader,创建catalinaDaemon
bootstrap.init();
daemon = bootstrap;
try {
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) { //...
} else if (command.equals("stopd")) { //...
} else if (command.equals("start")) {
// 调用catalinaDaemon的setAwait
daemon.setAwait(true);
// 调用catalinaDaemon的load
daemon.load(args);
// 调用catalinaDaemon的start
daemon.start();
} else if (command.equals("stop")) { //...
} else if (command.equals("configtest")) {//...
} else { }
} catch (Throwable t) {//...}
}
接下去看看Catalina的load,start方法是怎么实现的。
Catalina主要的作用就是根据server.xml创建一个server出来,然后启动server。
// org/apache/catalina/startup/Catalina.java
protected Server server = null;
public void load() {
// 创建一个digester用于解析server.xml。digester是一个用于解析xml文件的框架
Digester digester = createStartDigester();
// 获取server.xml文件
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
// 解析 解析结果是生成一个Server对象,赋值给server域
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource);
// 调用server的init方法
getServer().init();
}
public void start() {
if (getServer() == null) {
load();
}
getServer().start();
if (await) {
// await就是服务器创建一个serversocket监听一个端口,等这个端口发来SHUTDOWN关闭命令。
await();
stop();
}
}
public void await() {
getServer().await();
}
到这里启动脚本的工作就结束了,server.start()开始真正的服务器启动过程。
tomcat中有很多容器,connector,杂七杂八的一大堆,如果每个组件都在server.start方法中单独启动的话会变得十分繁杂混乱。而tomcat的各层container是呈树状的,所以如果能够自顶向下,通过递归的方式,由每一层的组件去通知下一层的组件进行启动,这样整体结构就会很清晰。为了实现这个功能,tomcat用了一个Lifecycle接口。
与Lifecycle接口相关的还有三个类,LifecycleState,LifecycleListener和LifecycleEvent。
public interface Lifecycle {
public LifecycleListener[] findLifecycleListeners();
public void removeLifecycleListener(LifecycleListener listener);
public void addLifecycleListener(LifecycleListener listener);
public void init() throws LifecycleException;
public void start() throws LifecycleException;
public void stop() throws LifecycleException;
public void destroy() throws LifecycleException;
}
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),
MUST_STOP(true, null),
MUST_DESTROY(false, null);
private final boolean available;
private final String lifecycleEvent;
}
public interface LifecycleListener {
public void lifecycleEvent(LifecycleEvent event);
}
public final class LifecycleEvent extends EventObject {
private final Object data;
private final String type;
}
这四个类之间的关系大致如下:基本上每个组件都实现了Lifecycle,通过实现start,stop等方法做出服务器启动,关闭时的对应操作。这些组件刚开始的状态都是NEW,调用start,init,stop方法后,它们的状态会发生变化。当状态发生变化时,会向组件中包含的LifecycleListener发送LifecycleState对应的LifecycleEvent。比如当组件调用init方法后,它的状态会从NEW变成INITIALIZING,同时向自身的所有LifecycleListener发送Lifecycle.BEFORE_INIT_EVENT类型的事件。
tomcat还提供了两个抽象类LifecycleBase重写了start等方法,规定了LifecycleState的转换过程。LifecycleSupport帮组件实现保存LifecycleListener,发送LifecycleEvent等操作。
下面以LifecycleSupport的start和init方法为例,分析其工作原理。
// LifecycleBase
private final LifecycleSupport lifecycle = new LifecycleSupport(this);
// 省略处理启动失败的代码
public final synchronized void start() throws LifecycleException {
// 组件初始状态为LifecycleState.NEW,所以先进行初始化
if (state.equals(LifecycleState.NEW)) {
init();
}
// 改变状态
setStateInternal(LifecycleState.STARTING_PREP, null, false);
try {
// 需要子类实现的抽象方法
startInternal();
} catch (Throwable t) {
//...
}
// 改变状态
setStateInternal(LifecycleState.STARTED, null, false);
}
public final synchronized void init() throws LifecycleException {
setStateInternal(LifecycleState.INITIALIZING, null, false);
try {
// 子类实现
initInternal();
} catch (Throwable t) {
//...
}
setStateInternal(LifecycleState.INITIALIZED, null, false);
}
private synchronized void setStateInternal(LifecycleState state,
Object data, boolean check) throws LifecycleException {
// 更新状态
this.state = state;
String lifecycleEvent = state.getLifecycleEvent();
if (lifecycleEvent != null) {
// 向监听器发送对应的事件
fireLifecycleEvent(lifecycleEvent, data);
}
}
根据上面的代码总结一下,调用start之后的状态变化过程如下:
INITIALIZING
initInternal()
INITIALIZED
STARTING_PREP
startInternal() 在其中干自己的事比如调用子类的start,然后设置状态为STARTING
STARTED
组件会实现LifecycleBase的各种Internal方法。所以每个组件在状态转换时干的事情只能写在两个地方:
接下去就看看各个Server和各个Container是怎么实现startInternal方法的,以及它们拥有的LifecycleListener干什么事。
StandardServer
// StandardServer
protected void startInternal() throws LifecycleException {
fireLifecycleEvent(CONFIGURE_START_EVENT, null);
setState(LifecycleState.STARTING);
globalNamingResources.start();
// Start our defined Services
synchronized (servicesLock) {
for (int i = 0; i < services.length; i++) {
// 主要就是调用service的start
services[i].start();
}
}
}
StandardService
service中的engine和connector都是在server.xml中配置好的,所以此时server.xml已经解析完,它的成员变量container和connectors都已经有值了。
// StandardService
// 存储Engine
protected Container container = null;
protected Connector connectors[] = new Connector[0];
// 用于存储映射关系的LifecycleListener
protected final MapperListener mapperListener =new MapperListener(mapper, this);
protected void startInternal() {
// 主要代码就这些
setState(LifecycleState.STARTING);
// 启动Engine
container.start();
for (Executor executor : executors) {
executor.start();
}
mapperListener.start();
for (Connector connector : connectors) {
connector.start();
}
}
StandardEngine
// StandardEngine
protected synchronized void startInternal() {
super.startInternal();
}
StandardEngine是继承ContainerBase,看一下它的startInternal()实现。
ContainerBase
protected synchronized void startInternal() throws LifecycleException {
// start 子容器们
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])));
}
for (Future<Void> result : results) {
result.get();
}
// start pipeline
if (pipeline instanceof Lifecycle)
((Lifecycle) pipeline).start();
// 改变状态,内部会发送监听事件
setState(LifecycleState.STARTING);
}
整体上就是start 子容器,然后改变状态,发送监听事件。
Engine自带的监听器有EngineConfig,其对LifecycleState.STARTING对应的事件默认只打日志。
StandardHost
// StandardHost
protected synchronized void startInternal() throws LifecycleException {
// 添加一个valve
String errorValve = getErrorReportValveClass();
if ((errorValve != null) && (!errorValve.equals(""))) {
try {
boolean found = false;
Valve[] valves = getPipeline().getValves();
for (Valve valve : valves) {
if (errorValve.equals(valve.getClass().getName())) {
found = true;
break;
}
}
if(!found) {
Valve valve =
(Valve) Class.forName(errorValve).newInstance();
getPipeline().addValve(valve);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString(
"standardHost.invalidErrorReportValveClass",
errorValve), t);
}
}
// 也是调用父类ContainerBase的startInternal
super.startInternal();
}
其startInternal主要也是通过父类实现的,所以也是先start children,然后通知监听器。但是host的children是一堆context,一般不配在server.xml中,所以此时它的children就是空的了。那就直接跳到通知监听器这个步骤。
Host自带的监听器是HostConfig,看一下它的事件处理方法。
// HostConfig
public void lifecycleEvent(LifecycleEvent event) {
// Process the event that has occurred
if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
check();
} else if (event.getType().equals(Lifecycle.START_EVENT)) {
start();
} else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
stop();
}
}
public void start() {
// 在host中部署context
if (host.getDeployOnStartup())
deployApps();
}
protected void deployApps() {
File appBase = host.getAppBaseFile();
File configBase = host.getConfigBaseFile();
String[] filteredAppPaths = filterAppPaths(appBase.list());
// Deploy XML descriptors from configBase
// 部署host对应的configBase中的context.xml描述符
deployDescriptors(configBase, configBase.list());
// 部署appBase中的war和dir
// Deploy WARs
deployWARs(appBase, filteredAppPaths);
// Deploy expanded folders
deployDirectories(appBase, filteredAppPaths);
}
到这里感觉start动作就没法从host传到context里面了,因为部署context操作是依靠事件监听机制的,在ContainerBase的start children之后。实际上在部署context的过程中,把context加到host的children的时候,会调用context的start方法,所以start动作又可以往下传播了。
StandardContext
StandardContext没有用到ContainerBase的startInternal方法。其监听器是ContextConfig
方法比较长,用文字大致描述一下从调用start开始其自身和监听器干的事
(ContextConfig:1)代表ContextConfig描述第1条。
初始化完后fireLifecycleEvent AFTER_INIT_EVENT (ContextConfig:1)
在startInternal之前调用,fireLifecycleEvent BEFORE_START_EVENT。(ContextConfig:2)
(从这里开始startInternal)创建WebResourceRoot
创建WebappLoader,调用它的start,把Context中的WebResourceRoot设置到webClassLoader中,这个start内部又会调用其中的webClassLoader的start。
创建SessionManager
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null)调用ContextConfig的监听方法,解析web.XML(ContextConfig:3)
调用children的start
Call ServletContainerInitializers
call application event listeners
Start manager
构建filter
处理loadOnStartup servlet
start结束,fireLifecycleEvent AFTER_START_EVENT (ContextConfig 4)
ContextConfig:
StandardWrapper
基本不干啥。
到此就把Container部分都分析完了。
Connector主要是创建一些监听请求的线程,mapperListener是把整个service容器中的host,context,wrapper和请求url的映射关系都存起来。这样在请求到来的时候就把对应的host,context和wrapper都解析出来存到request里面。具体的之后再分析吧。。