1. 程式人生 > >Tomcat的頂層結構及啟動過程

Tomcat的頂層結構及啟動過程

寫在前面:

學習是什麼?學習就是模仿,重複,實踐,總結。

最近看的這本書裡面有tomcat的簡單原始碼部分,就研究一下,會有收穫的。

這幾天在看原始碼是用的是idea這款工具,幾個月前還是挺抵觸它的,感覺它沒有eclipse好用,但是本著挑戰未知領域的精神使用了3天,已經慢慢喜歡上這款工具了,推薦一下。

文章開始:

Server           伺服器 Service          服務 Connector     聯結器 Container      容器 頂層結構圖:


Tomcat中最頂層的容器叫server,代表整個伺服器,Server中至少包含一個Service,用於提供具體的服務。Service包含倆部分:container和connector。container用於封裝和管理servlet,以及具體的request請求。Connector用於處理連線相關的事情,並提供Socket與request,response的轉換。

它們之間的關係:(服務於伺服器,server與service)

一個tomcat中只有一個server 一個server可以有多個service 一個service只有一個Container,但是可以有多個Connectors,因為一個服務可以有多個連線,如提供http和https連線,也可以提供相同協議不同埠的連線 Bootstrap是Tomcat的入口,正常情況下啟動Tomcat就是呼叫Bootstrap的main方法

單詞解釋:
    catalina可以理解為一個Servlet容器
    await  等待
實現類:

    org.apache.catalina.startup.Bootstrap

程式碼開始(篇幅問題,只說最主要的程式碼)

Bootstrap的main方法如下:

private static Bootstrap daemon = null;
private Object catalinaDaemon = null;

public static void main(String[] args) {
    //新建一個Bootstrap
    if(daemon == null) {
        Bootstrap t = new Bootstrap();

        try {
            //初始化了ClassLoader,並用ClassLoader建立了Catalina例項,並賦值給catalinaDaemon變數
            t.init();
        } catch (Throwable var3) {
            handleThrowable(var3);
            var3.printStackTrace();
            return;
        }

        daemon = t;
    } else {
        Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
    }

    try {
        String t2 = "start";
        if(args.length > 0) {
            t2 = args[args.length - 1];
        }

        if(t2.equals("startd")) {
            args[args.length - 1] = "start";
            daemon.load(args);
            daemon.start();
        } else if(t2.equals("stopd")) {
            args[args.length - 1] = "stop";
            daemon.stop();
        } else if(t2.equals("start")) {
            daemon.setAwait(true);
            daemon.load(args);
            daemon.start();
        } else if(t2.equals("stop")) {
            daemon.stopServer(args);
        } else if(t2.equals("configtest")) {
            daemon.load(args);
            if(null == daemon.getServer()) {
                System.exit(1);
            }

            System.exit(0);
        } else {
            log.warn("Bootstrap: command \"" + t2 + "\" does not exist.");
        }
    } catch (Throwable var4) {
        Throwable t1 = var4;
        if(var4 instanceof InvocationTargetException && var4.getCause() != null) {
            t1 = var4.getCause();
        }

        handleThrowable(t1);
        t1.printStackTrace();
        System.exit(1);
    }

}
main方法包含倆部分內容:
    1、新建Bootstrap,並執行init方法
    2、處理main方法中傳入的命令,如果args引數為空,則預設執行start方法

新建Bootstrap後會執行init方法,也就是上面程式碼中的init方法:

public void init() throws Exception {
    初始化了ClassLoader
    this.initClassLoaders();
    Thread.currentThread().setContextClassLoader(this.catalinaLoader);
    SecurityClassLoad.securityClassLoad(this.catalinaLoader);
    if(log.isDebugEnabled()) {
        log.debug("Loading startup class");
    }

    Class startupClass = this.catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
    並用ClassLoader建立了Catalina例項
    Object startupInstance = startupClass.newInstance();
    if(log.isDebugEnabled()) {
        log.debug("Setting startup class properties");
    }

    String methodName = "setParentClassLoader";
    Class[] paramTypes = new Class[]{Class.forName("java.lang.ClassLoader")};
    Object[] paramValues = new Object[]{this.sharedLoader};
    Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
    method.invoke(startupInstance, paramValues);
    並賦值給catalinaDaemon變數
    this.catalinaDaemon = startupInstance;
}

這個方法主要做了三件事:
    1、初始化了ClassLoader
    2、用ClassLoader建立了Catalina例項,並賦值給catalinaDaemon變數
    3、使用catalinaDaemon來執行命令操作

該main方法當為空參時,也就是預設執行start命令:

它執行了下面三個方法:這個三個方法內部都呼叫了Catalina的相關方法進行具體執行,而且都是用反射來執行的。
  

  daemon.setAwait(true);
  daemon.load(args);
  daemon.start();
以start方法為例:

public void start() throws Exception {
    if(this.catalinaDaemon == null) {
        this.init();
    }

    Method method = this.catalinaDaemon.getClass().getMethod("start", (Class[])null);
    method.invoke(this.catalinaDaemon, (Object[])null);
}
做了三件事:
    1、判斷catalinaDaemon是否初始化,如果沒有則對其初始化
    2、使用Method進行反射呼叫它的空參start方法(涉及反射相關知識點內容後期會進行補充)
    3、反射後的呼叫效果就是:((Catalina)catalinaDaemon).start()
而daemon.setAwait(true)與daemon.load(args)這倆個方法也是用類似的方法呼叫了Catalina中的setAwait和load方法
下一篇學習Catalina的啟動過程