Tomcat 源碼分析(一)——啓動與生命週期組件

寫在前面的話:讀Tomcat源碼也有段時間了,大領悟談不上。一些當心得記錄下來,供你們參考相護學習。apache

1、啓動流程bootstrap

Tomcat啓動首先須要熟悉的是它的啓動流程。和初學者第一天開始寫Hello World同樣,Tomcat的啓動也依賴main方法。設計模式

 1 /*
 2  * org.apache.catalina.startup.Bootstrap
 3  */
 4 if (daemon == null) {
 5     Bootstrap bootstrap = new Bootstrap(); // 實例對象
 6     try {
 7         bootstrap.init(); // 初始化
 8     } catch (Throwable t) {
 9         handleThrowable(t);
10         return;
11     }
12     daemon = bootstrap;
13 } else {
14     Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
15 }

實例化Bootstrap以後,首先須要對它初始化。初始化的流程很長,可是省略掉細節其實就是作了兩件事:工具

(1)爲當前線程建立類加載器:學習

initClassLoaders()

 

(2)經過反射實例化一個Catalina對象(Tomcat組件的實際管理者):spa

Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();

 

 初始化之後daemon和catalinaDaemon就完成了賦值。繼續main方法往下線程

 1 String command = "start";
 2 if (args.length > 0) {
 3     command = args[args.length - 1];
 4 }
 5 if (command.equals("startd")) {
 6     args[args.length - 1] = "start";
 7     daemon.load(args); // 加載
 8     daemon.start(); // 啓動
 9 } else if (command.equals("stopd")) {
10     args[args.length - 1] = "stop";
11     daemon.stop();
12 } else if (command.equals("start")) {
13     daemon.setAwait(true);
14     daemon.load(args);
15     daemon.start();
16 } else if (command.equals("stop")) {
17     daemon.stopServer(args);
18 }

首先執行加載,依然是使用反射調用Catalina對象的load()方法。方法裏面的代碼不少第一次看的同窗確定會有點蒙,仔細分析一下:debug

(1)讀取conf/server.xml文件並解析InputStream:設計

Digester是一個xml文件解析工具,目前屬於Apache的Jakarta項目。使用Digester解析xml文件代碼量少也很是簡單。
code

(2)初始化Server

Server serv = getServer();
serv.init();

 

接下來咱們看看server.xml的文檔結構,裏面包含了幾個重要的節點:Server、Listener、Service、Connector、Engine、Realm、Host。初始化的過程其實就是將這些節點所表明的對象逐一初始化。Server是其它節點的根節點,因此首先對它執行init()操做。

2、生命週期管理

Tomcat中的許多對象都實現了Lifecycle接口,裏面定義了12個狀態,類關係圖以下:

LifecycleBase實現了Lifecycle並引入LifecycleState和LifecycleSupport,LifecycleState是一個枚舉類,將12個狀態除NEW和FAILED之外分別將狀態和事件對應,並經過事件觸發監聽器。LifecycleSupport集中管理各類監聽器。Tomcat經過4個方法管理這些狀態:init() 、start()、stop()、destroy(),而LifecycleBase實現了init() 、start()、stop()、destroy()又暴露了4個接口initInternal()、startInternal()、stopInternal()、destroyInternal()。全部生命週期對象都須要實現以上4個方法,是設計模式中的模板模式。

初略分析了一下Tomcat對於組件的管理方式,接下來再回到Catalina的load()方法。方法中經過getServer()獲取StandardServer實例,並執行initInternal()方法。在server.xml配置文件中,server節點能夠包含多個service

for (int i = 0; i < services.length; i++) {
    services[i].init(); // 初始化StandardService
}

在StandardService的initInternal()方法中繼續初始化Connector和Executor,重點代碼以下:

 1 synchronized (connectorsLock) {
 2     for (Connector connector : connectors) {
 3         try {
 4             connector.init();
 5         } catch (Exception e) {
 6             String message = sm.getString(
 7                     "standardService.connector.initFailed", connector);
 8             log.error(message, e);
 9 
10             if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
11                 throw new LifecycleException(message);
12         }
13     }
14 }

 

當全部組件初始化完成以後執行啓動流程:

 1 try {
 2     // getServer().start();
 3     Server srv = getServer();
 4     srv.start(); // 啓動流程...
 5 } catch (LifecycleException e) {
 6     log.fatal(sm.getString("catalina.serverStartFail"), e);
 7     try {
 8         getServer().destroy();
 9     } catch (LifecycleException e1) {
10         log.debug("destroy() failed for failed Server ", e1);
11     }
12     return;
13 }

和initInternal()流程相似,startInternal()方法也是逐步調用,就不在文章中一一分析了。感興趣的同窗要想深刻探究請經過源碼debug分析。

 

總結:Tomcat啓動流程實質上就是對各個生命週期組件的管理並經過Digester解析xml文件,這些組件在不一樣的生命週期狀態又分別對應不一樣的響應事件,監聽器經過響應事件驅動也方便了開發者的二次擴展值得認真學習。

相關文章
相關標籤/搜索