这一次将会对tomcat的启动过程进行一个分析。
一、main函数分析
tomcat的入口函数是:org.apache.catalina.startup.main
在这个函数中最关键的代码段是:
public static void main(String args[]) {
//......
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();
} 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.");
}
//......
}
从这里我们可以看到,当启动参数为“start”的时候,实际调用的是Bootstrap.start()函数(daemon是一个Bootstrap类的实例),接下来分析start()函数。
二、start函数分析
Bootstrap.start()函数的代码:
public void start()
throws Exception {
if( catalinaDaemon==null ) init();
Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
method.invoke(catalinaDaemon, (Object [])null);
}
可以看出本质上调用的是catalinaDaemon的start函数,那么catalinaDaemon又是哪个类的实例呢?通过下面这段代码可以发现:
public void init()throws Exception
{
// ......
Class<?> startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();
//......
catalinaDaemon = startupInstance;
}
所以代码接下来将会走到org.apache.catalina.startup.Catalina.start()中去,
public void start() {
if (getServer() == null) {
load();
}
//......
getServer().start();
//......
if (await) {
await();
stop();
}
}
在这里最关键的两个函数是:
1、load(),配置文件的加载
2、getServe日().start(),服务的启动。
还有一个地方需要注意的地方是await()函数,这个函数实际上是一直在监听shutdown命令,默认是8005端口,配置文件中可以设置。
三、配置文件加载
tomcat的配置文件加载是在org.apache.catalina.startup.Catalina.load()函数里面完成的,代码为:
public void load() {
//......
Digester digester = createStartDigester();
//......
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource);
//......
getServer().init();
//......
}
这个函数中主要干了三件事情:
1、构建了一个xml解析执行配置
2、解析配置文件,并根据第一步生成的构建方式来构建tomcat的所有实例。
3、初始化服务,这个服务实例是在第二步中构建的。初始化的逻辑过程和启动过程的逻辑非常相似,具体逻辑可以参考启动过程。
具体的加载过程将会在下一篇博客中进行分析。
四、服务的启动过程
在start函数中会调用getServer().start()函数,就是启动tomcat服务。通过分析可以知道getServer函数返回的是org.apache.catalina.core.StandardServer类的实例。start函数是在其基类org.apache.catalina.util.LifecycleBase中实现的,其关键代码为:
@Override
public final synchronized void start() throws LifecycleException {
//......
startInternal();
//......
}
这个函数的本质是调用了startInternal函数,在StandardServer类中有实现这个函数,其关键代码为:
@Override
protected void startInternal() throws LifecycleException {
// ......
synchronized (services) {
for (int i = 0; i < services.length; i++) {
services[i].start();
}
}
}
可以看出,在server的启动过程中实际上调用的是每一个services的start函数,这里的services默认都是StandardService的实例,而StandardService也是继承LifecycleBase的,所以本质上会再调用StandardService的startInternal函数,这个函数的关键代码:
@Override
protected void startInternal() throws LifecycleException {
//......
if (container != null) {
synchronized (container) {
container.start();
}
}
//......
synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}
//......
synchronized (connectors) {
for (Connector connector: connectors) {
try {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connector), e);
}
}
}
}
这个函数中主要做了三件事情:
1、调用container的start函数,container默认是org.apache.catalina.core.StandardEngine的实例,它也是继承LifecycleBase的。
2、调用所有的Executor的start函数,Executor 也是继承LifecycleBase的。
3、调用所有的connector的start函数,connector是org.apache.catalina.connector.Connector的实例,它也是继承LifecycleBase的。
container的start代码本质上是执行的ContainerBase.startInternal函数,其关键代码为:
@Override
protected synchronized void startInternal() throws LifecycleException {
// ......
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).start();
logger = null;
getLogger();
if ((manager != null) && (manager instanceof Lifecycle))
((Lifecycle) manager).start();
if ((cluster != null) && (cluster instanceof Lifecycle))
((Lifecycle) cluster).start();
Realm realm = getRealmInternal();
if ((realm != null) && (realm instanceof Lifecycle))
((Lifecycle) realm).start();
if ((resources != null) && (resources instanceof Lifecycle))
((Lifecycle) resources).start();
// Start our child containers, if any
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<Future<Void>>();
for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
}
boolean fail = false;
for (Future<Void> result : results) {
try {
result.get();
} catch (Exception e) {
log.error(sm.getString("containerBase.threadedStartFailed"), e);
fail = true;
}
}
//......
if (pipeline instanceof Lifecycle)
((Lifecycle) pipeline).start();
setState(LifecycleState.STARTING);
// Start our thread
threadStart();
}
在这里同样是调用了一大堆LifecycleBase实例的start函数进行启动。
connector的start调用的关键代码:
@Override
protected void startInternal() throws LifecycleException {
//......
protocolHandler.start();
//......
}
这里最关键的是调用了http的handler的start,启动了以太网服务器,等待接收数据,具体的启动和执行原理将在以后分析。
至此tomcat的启动过程已经初步分析玩了。