先给出一张Tomcat标准启动时序图(标准启动是从 bin 中的启动文件启动 Tomcat):
Tomcat 的启动非常标准,除去 Boostrap、Catalin,Server、Service 等这些组件与server.xml 都是一一对照,同时又有先后顺序。基本的顺序是先 init(),然后再 start()。
除了 Bootstrap、Catalina,其他的 Server、Service 等都只是一个接口,实现类均为 StandardXXX 。
下面按照从脚本启动的顺序,一步步分析Tomcat的启动过程。
结论:startup.bat调用catalina.bat,传入start;catalina.bat在start分支中使用java Bootstrap 命令函参数 start
启动Tomcat。
我们启动Tomcat时,是在Tomcat中bin目录中,点击startup.bat(如果是Windows的话,当然Linux使用startup.sh)。通过下面的分析知道,启动tomcat也可以不用startup.bat,直接调用catalina.bat start。
startup.bat做的事情是:
我们分片段看catalina.bat文件:
片段1:
最后tomcat启动是执行了Bootstrap类中的main方法。
通过上面脚本分析知道,Tomcat标准启动调用Bootstap.main(),传入参数是start。
Bootstrap.main()
public static void main(String args[]) {
synchronized (daemonLock) {
if (daemon == null) {
// main第一次执行时,daemon为null,new Bootstrap(),并执行init。
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t); t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
}
try {
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
} else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
if (null == daemon.getServer()) {
System.exit(1);
}
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else if (command.equals("configtest")) {
daemon.load(args);
if (null == daemon.getServer()) {
System.exit(1);
}
System.exit(0);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
// Unwrap the Exception for clearer error reporting
if (t instanceof InvocationTargetException &&
t.getCause() != null) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit(1);
}
}
上面main()分为2块:
else if (command.equals("start"))
分支。下面先看load过程:
接下来转到Catalina.load():
Server.init()会调用到StandardServer.initInternal():
Service.init()会调用到StandardService.initInternal():
Engine.init()会调用到StandardEngine.init():
以StandardServer类为例,发现没有init(),只有一个类似于 init 的 initInternal(),这是为什么呢?
@Override
protected void initInternal() throws LifecycleException {
...
}
首先看下Tomcat各组件的生命周期管理。
Tomcat 的架构设计是清晰的、模块化的。Tomcat拥有很多组件,在启动时一个一个组件启动,很容易遗漏组件,同时还会给后面动态组件拓展带来麻烦。如果采用传统方式的话,组件在启动过程中如果发生异常,会很难管理,比如下一个组件调用了 start(),但是如果它的上级组件还没有 start 甚至还没有 init 的话,Tomcat 的启动会非常难管理。
因此,Tomcat 的设计者提出一个解决方案:用 Lifecycle 管理启动,停止、关闭。
Tomcat 内部架构中各个核心组件有包含与被包含关系,如:Server 包含了 Service,Service 又包含了 Container、Connector,这个结构有一点像数据结构中的树。所以,我们可以通过父容器启动它的子容器,这样只要启动根容器,就可以把其他所有容器都启动,从而达到统一的启动,停止、关闭的效果。
所有这些组件有一个统一的接口——Lifecycle,把所有的启动、停止、关闭等生命周期相关的方法都组织到一起,可以很方便管理 Tomcat 各个容器组件的生命周期。
Lifecycle 其实就是定义了一些状态常量和几个方法,主要方法是 init、start、stop。Lifecycle 使用的是模板设计模式。
public interface Lifecycle {
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 void addLifecycleListener(LifecycleListener listener);
public LifecycleListener[] findLifecycleListeners();
public void removeLifecycleListener(LifecycleListener listener);
public void init() throws LifecycleException;
public void start() throws LifecycleException;
public void stop() throws LifecycleException;
public void destroy() throws LifecycleException;
public LifecycleState getState();
public String getStateName();
public interface SingleUse {
}
}
Tomcat 的 Server 组件的 init() 负责遍历调用其包含所有的 Service 组件的 init(),但是Server 的实现类 StandardServer 没有 init(),那么init() 是在哪里? 其实是在它的父类 LifecycleBase中,这个类负责统一的生命周期管理。
所以 StandardServer 会调用到 initInternal(),这个方法会初始化子容器 Service 的 init()。