Tomcat 學習進階歷程之Tomcat啟動過程分析
本節通過跟蹤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、呼叫
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
前面提到了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已經配置好自身,可以對外工作了。整個啟動時序圖如下: