寫在前面的話:讀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文件,這些組件在不一樣的生命週期狀態又分別對應不一樣的響應事件,監聽器經過響應事件驅動也方便了開發者的二次擴展值得認真學習。