本节通过跟踪Tomcat的源码来分析Tomcat是如何启动及装配各个组件的。最好下载Tomcat的源码导入到Eclipse,这样方便跟踪。方法可参考:
http://linux.it.net.cn/e/server/Tomcat/2014/1225/10741.html
在Tomcat的启动脚本篇,我们分析过,当执行Start.bat文件时,最后实际调用的是BootStrap.java类。如下图:
如上图,实际调用BootStrap,并传递一个名为‘start’参数。
在BootStrap的主方法main中,主要做了一下几件事情:
1、实例化一个BootStrap,并调用其init方法。
2、调用load方法。
3、调用start方法。
在init方法中,BootStrap完成了一件重要的工作,就是根据Java反射,实例化了一个org.apache.catalina.startup.Catalina类。Init的主要代码如下:
-
initClassLoaders();
-
Thread.currentThread().setContextClassLoader(catalinaLoader);
-
SecurityClassLoad.securityClassLoad(catalinaLoader);
-
-
if (log.isDebugEnabled())
-
log.debug("Loading startup class");
-
Class startupClass =
-
catalinaLoader.loadClass
-
("org.apache.catalina.startup.Catalina");
-
Object startupInstance = startupClass.newInstance();
-
-
if (log.isDebugEnabled())
-
log.debug("Setting startup class properties");
-
String methodName = "setParentClassLoader";
-
Class paramTypes[] = new Class[1];
-
paramTypes[0] = Class.forName("java.lang.ClassLoader");
-
Object paramValues[] = new Object[1];
-
paramValues[0] = sharedLoader;
-
Method method =
-
startupInstance.getClass().getMethod(methodName, paramTypes);
-
method.invoke(startupInstance, paramValues);
-
-
catalinaDaemon = startupInstance;
BootStrap的load方法,通过反射,调用Catalina类的load方法。在Catalina类的load方法中完成了一项极其重要的工作,就是通过Apache的另一项开源项目Digester来解析Tomcat的核心配置文件:conf/server.xml。 Digester作用是讲XML转成指定的Java对象。Catalina类的load方法的具体工作下面会介绍到。
再说BootStrap的start方法,主要是调用Catalina类的start方法。
通过对BootStrap三个方法的分析可以看到,BootStrap类启动时的主要工作就是实例化Catalina,调用其load方法与start方法。并且这些操作都是在BootStrap类中通过Java反射机制来完成的。
前面提到了Catalina的load方法通过Apache的另一项开源项目Digester来解析Tomcat的核心配置文件:conf/server.xml。Catalina定义了一个名为createStartDigester的方法,此方法根据server.xml的结构,定义了一套解析Server.xml文件的柜子,并返回一个实例化的Digester。在load方法使用返回的Digester示例加载server.xml配置文件,并进行解析。Load方法的主要代码如下:
-
Digester digester = createStartDigester();
-
InputSource inputSource = null;
-
InputStream inputStream = null;
-
try {
-
file = configFile();
-
inputStream = new FileInputStream(file);
-
inputSource = new InputSource("file://" + file.getAbsolutePath());
-
} catch (Exception e) {
-
;
-
}
-
-
try {
-
inputSource.setByteStream(inputStream);
-
-
digester.push(this);
-
digester.parse(inputSource);
-
inputStream.close();
-
} catch (Exception e) {
-
log.warn("Catalina.start using "
-
+ getConfigFile() + ": " , e);
-
return;
-
}
-
getServer().initialize();
在load方法中,通过调用digester.push(this); Catalina将自身设置进digester,这样通过digester.parse方法解析后,Catalina对象的各个属性将被实例化并填充。最关键的是实例化了Server对象。实际上digester实例化了Service、Connector、Engine、Host、context等各种组件,并相应的设置了各个组件的关系。
Load方法在最后调用了server的initialize方法。在Tomcat中Server接口的标准实现是StandardServer。具体是那个实现类,就看Catalina类createStartDigester方法里面定义的了。StandardServer的initialize方法对其子组件service进行了初始化。
-
-
for (int i = 0; i < services.length; i++) {
-
services[i].initialize();
-
}
Service在Tomcat中的标准实现是StandardService。其initialize方法主要代码如下:
-
synchronized (connectors) {
-
for (int i = 0; i < connectors.length; i++) {
-
try {
-
connectors[i].initialize();
-
} catch (Exception e) {
-
String message = sm.getString(
-
"standardService.connector.initFailed",
-
connectors[i]);
-
log.error(message, e);
-
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
-
throw new LifecycleException(message);
-
}
-
}
-
}
可以看到Service的initialize主要工作就是调用connector的initialize方法。
同理Connector会依次处理其下的其他组件,这里不再依次列入。
让我们回到Catalina,再看看它的start方法:
-
if (getServer() instanceof Lifecycle) {
-
try {
-
((Lifecycle) getServer()).start();
-
} catch (LifecycleException e) {
-
log.error("Catalina.start: ", e);
-
}
-
}
StandardServer的start方法关键代码如下:
-
synchronized (services) {
-
for (int i = 0; i < services.length; i++) {
-
if (services[i] instanceof Lifecycle)
-
((Lifecycle) services[i]).start();
-
}
-
}
同样的StandardService会依次启动其下的其他组件,此处不再依次列出。
等Catalina的Start方法执行完,表明Tomcat已经配置好自身,可以对外工作了。整个启动时序图如下:
(责任编辑:IT) |