Tomcat的頂層結構及啟動過程
阿新 • • 發佈:2018-12-12
寫在前面:
學習是什麼?學習就是模仿,重複,實踐,總結。
最近看的這本書裡面有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方法如下:
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); } }
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的啟動過程