本节通过跟踪Tomcat的源码来分析Tomcat是如何启动及装配各个组件的。最好下载Tomcat的源码导入到Eclipse,这样方便跟踪。方法可参考:
http://www.cnblogs.com/huangfox/archive/2011/10/20/2218970.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();//初始化catalinaLoader
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
Class startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();
// Set the shared extensions class loader
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是BootStrap类的一个私有变量
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();//根据catalina.base路径获取server.xml文件
inputStream = new FileInputStream(file);
inputSource = new InputSource("file://" + file.getAbsolutePath());
} catch (Exception e) {
;
}
//…….略去一些源码
try {
inputSource.setByteStream(inputStream);
//Catalina将自身设置进digester
digester.push(this);
digester.parse(inputSource);
inputStream.close();
} catch (Exception e) {
log.warn("Catalina.start using "
+ getConfigFile() + ": " , e);
return;
}
getServer().initialize();//初始化Server
在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 进行了初始化。
// Initialize our defined Services
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已经配置好自身,可以对外工作了。整个启动时序图如下:
上一篇:Tomcat 学习进阶历程之Tomcat架构与核心类分析