Tomcat8源碼分析-啓動流程-load方法

上一篇:Tomcat8源碼分析-類結構圖html

前面已經將Tomcat的類結構和架構介紹了一下,如今經過DEBUG代碼看看代碼是如何一層層調用的,下圖爲啓動過程當中的準備+load詳細過程java

 

詳細調用過程-時序圖

說明

    從整個圖中能夠觀察到出現頻次最高的就是init\initInternal\fireLifecycleEvent,在類圖中也特地羅列了出來,說明主體流程就是靠這些方法來完成的。這三個方法的調用就是一個模板,即:init(LifecycleBase)->setStateInternal(LifecycleBase)->initInternal(StandardXxx)->setStateInternal(LifecycleBase),能夠說幾乎全部關鍵的實現都是在initInternal和LifecycleListener中完成的。web

    啓動從一個BootStrap類開始,先執行它的靜態塊(完成了Tomcat工做目錄初始化),BootStrap.main方法接着就調用BootStrap.init方法(肯定三個ClassLoader,Catalina類實例化),再而後就經過反射調用Catalina.load方法,開始了加載過程。apache

     再還沒看源碼以前,猜測一下Tomcat啓動過程當中是否是應該有以下這些動做:設計模式

        1.獲取server.xml緩存

        2.解析server.xml網絡

        3.處理Tomcat本身內部默認的一些配置或者約束或者規則什麼的多線程

        4.根據解析的結構生成Tomcat架構對應的實例結構 架構

        5.既然要接收請求,須要綁定與監聽端口app

        6.找到目錄下有哪些應用

        7.解析每一個應用下的web.xml文件

        8.處理Servlet\Servlet-Mappping\Filter

        9.等一切都準備就緒,Tomcat如何接收到請求,將請求封裝爲request,將request傳給誰處理,處理以後又如何封裝爲request,如何發送回客戶端

        10.經過什麼BIO\NIO\NIO2等等IO方式完成網絡操做

    下面就看看load過程當中有作的事情,包含了以上那些步驟。

雖然知道有一些StandardXxx類,可是稍微想一想也不會是由這些類去完成server.xml的定位和解析,由於它本身自己就須要解析配置完成,那麼在類圖中能夠看到是有Catalina.configFile()這行代碼,最終看到了熟悉的"server.xml",看到這裏是否是接下來就應該執行解析了喃?往下看。。。

file = configFile();

    protected File configFile() {

        File file = new File(configFile);
        if (!file.isAbsolute()) {
            file = new File(Bootstrap.getCatalinaBase(), configFile);
        }
        return file;

    }

    protected String configFile = "conf/server.xml";

代碼繼續往下翻以前能夠先看看這段代碼:

Digester digester = createStartDigester()

Digester(有道詞典翻譯是蒸煮器,煮解器),我就把它叫作解析器吧,畢竟它後面又用parse()完成對server.xml的解析。剛點進去看的時候,有點被嚇着了,都是一大堆不認識的鬼。。。摘了部分代碼以下:

digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");
        digester.addObjectCreate("Server/Service",
                                 "org.apache.catalina.core.StandardService",
                                 "className");
        digester.addObjectCreate("Server/Service/Executor",
                         "org.apache.catalina.core.StandardThreadExecutor",
                         "className");
        //這裏面還會添加不少規則,好比設置EngineConfig
        digester.addRuleSet(new EngineRuleSet("Server/Service/"));
        //這裏面還會添加不少規則,好比設置HostConfig(它會負責完成Context組件實例化和應用部署)
        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));

這樣看上去就不會那麼恐怖了,見到了熟悉的StandardServer,StandardService,能夠猜測在解析的時候,是否是根據server.xml中的標籤名字會從這裏拿到對應類的全路徑反射生成實例喃?看一張動圖(3秒一幀)

接着往下,上面準備了規則(配置文件中對應標籤應該實例化那些類,默認添加了那些監聽器給對應的標籤),找到了server.xml文件路徑,接下來就要真正的解析和處理了,看代碼

try {
                inputSource.setByteStream(inputStream);
                digester.push(this);
                //關鍵理解點2:將Catalina對象傳給解析器,解析器完成以後,Server就已經有值了。如:
                // Server-Service-Engine-Host已經完成實例化。
                // Server-Service-Connector-(HTTP/AJP)ProtocolHandler-EndPoint
                //將斷點放到這裏看先後server的層級,能夠匹配上Tomcat架構圖
                digester.parse(inputSource);
            } catch (SAXParseException spe) {
                ...省略....
            } catch (Exception e) {
                ...省略....
            }
        } finally {
            ...省略....
        }

這部分代碼執行完,Server就已經實例化完了,但不是全部的內部屬性都構造好了,還有很長的路要走好比init/start。

既然Server已經有了,再仔細看看它的內部結構,上動圖

能夠看到Server-Service-Engine-Host,Server-Service-Connector這兩條線都已經有了,可能會問怎麼還沒看到Context-Wrapper喃,由於那是在start階段完成的。

parse()方法裏面的具體邏輯就不須要去剖析了,只須要知道它完成了Server-Service-Engine-Host等等的實例化,並添加了監聽和Valve規則(這裏看不懂不要緊,等後面就會知道了)。

到此爲止,Server的內部結構雛形已經出來了,再看看往下的server.init-engint.init等等xxx.init是如何一層層調用的

代碼往下走,看到了service.init

// Start the new server
        try {
            //Server Init操做,在這裏會一層層的往下Init。Server-Service-Engine/Connector-Host
            getServer().init();
        } catch (LifecycleException e) {
            if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                throw new java.lang.Error(e);
            } else {
                log.error("Catalina.start", e);
            }
        }

從這裏開始就是模板方法設計模式開始發功的時候了,若是還不知道的話,趕忙點進去看看,並動手寫寫。有經驗的話能夠想起在JDK的不少源碼都能看到這種設計模式,好比多線程中的同步器,還有Spring提供的BeanDefinitionRegistryPostProcessor擴展點等。

在看動圖以前下描述一下:凡是LifecycleBase的子類,調用的init方法必定是LifecycleBase.init(由於該方法被聲名爲final了),裏面就是setStateInternal-fireLifecycleEvent-initInternal-setStateInternal-fireLifecycleEvent這樣的套路。看到這一點很重要,不然往下DEBUG腦袋會發蒙的。

下面這張動圖描述了Server-Service-Engine-Host這條線,至於Server-Service-Engine-Connector這條線是一樣的方式。

總結:

1、Catalina在調用StandardServer.init以前完成了對server.xml解析,生成的Server對象的內部層次結構,初始化了默認的一些參數對應的實例

2、StandardServer.init執行以後完成了以下幾件事情:
1.Server-Service-Engine-Connector的狀態都變成了INITIALIZED

2.Connector中HTTP/AJP協議對應的ProtocolHandler也都實例化完成,監聽啓動完成

如圖:

但此時是沒法訪問Tom貓的主頁的,由於還沒start,也就是尚未部署應用。假設你嘗試使用http://localhost:8080去訪問,會一直沒有響應。

3.MapperListener初始化完成

MapperListener經過監聽容器事件來完成對容器的註冊和取消註冊。而Mapper用於對容器進行緩存和管理,同時提供uri映射的功能

在這裏推薦兩個博客:

深刻理解Tomcat

愛吃魚的KK(裏面有不少深刻的分析)

 

下一篇:Tomcat8源碼分析-啓動流程-start方法

相關文章
相關標籤/搜索