1. 程式人生 > >Tomcat 學習進階歷程之Tomcat啟動過程分析

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、呼叫

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;

BootStrapload方法,通過反射,呼叫Catalina類的load方法。在Catalina類的load方法中完成了一項極其重要的工作,就是通過Apache的另一項開源專案Digester來解析Tomcat的核心配置檔案:conf/server.xml Digester作用是講XML轉成指定的Java物件Catalina類的load方法的具體工作下面會介紹到。

再說BootStrapstart方法,主要是呼叫Catalina類的start方法。

通過對BootStrap三個方法的分析可以看到,BootStrap類啟動時的主要工作就是例項化Catalina,呼叫其load方法與start

方法。並且這些操作都是在BootStrap類中通過Java反射機制來完成的。

前面提到了Catalinaload方法通過Apache的另一項開源專案Digester來解析Tomcat的核心配置檔案:conf/server.xmlCatalina定義了一個名為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例項化了ServiceConnectorEngineHostcontext等各種元件,並相應的設定了各個元件的關係。

Load方法在最後呼叫了serverinitialize方法。在TomcatServer介面的標準實現是StandardServer。具體是那個實現類,就看CatalinacreateStartDigester方法裡面定義的了。StandardServerinitialize方法對其子元件service進行了初始化。
// Initialize our defined Services
        for (int i = 0; i < services.length; i++) {
            services[i].initialize();
        }

ServiceTomcat中的標準實現是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);
                }
            }
        }

可以看到Serviceinitialize主要工作就是呼叫connectorinitialize方法。

同理Connector會依次處理其下的其他元件,這裡不再依次列入。

讓我們回到Catalina,再看看它的start方法:

if (getServer() instanceof Lifecycle) {
            try {
                ((Lifecycle) getServer()).start();
            } catch (LifecycleException e) {
                log.error("Catalina.start: ", e);
            }
        }

StandardServerstart方法關鍵程式碼如下:

synchronized (services) {
            for (int i = 0; i < services.length; i++) {
                if (services[i] instanceof Lifecycle)
                    ((Lifecycle) services[i]).start();
            }
        }

同樣的StandardService會依次啟動其下的其他元件,此處不再依次列出。

CatalinaStart方法執行完,表明Tomcat已經配置好自身,可以對外工作了。整個啟動時序圖如下:

啟動時序圖