tomcat的啓動是經過Bootstrap類的main方法(tomcat6開始也能夠直接經過Catlina的main啓動)java
Bootstrap的main方法先new了一個本身的對象(Bootstrap),而後用該對象主要執行了四個方法:web
init();apache
setAwait(true);數組
load(args);瀏覽器
start();tomcat
init():初始化了ClassLoader(類加載器,沒它的話後面就無法加載其餘類了),而後用ClassLoader建立了Catlina實例。安全
setAwait(true),load(args),start();這三個方法實際上都是經過反射調用的Catlina實例中所對應的這三個方法。服務器
首先Catlina中的setAwait(true)方法:網絡
這個方法設置的值(true或false),是留給後面用的。當經過後面的start()方法啓動完服務器後,會檢查這個值爲true仍是false,若是爲true,調用Catlina的await()方法,tomcat繼續開着。若是爲false,則調用Catlina的stop()方法,關閉tomcat。session
而後是Catlina中的load(args)方法:
Catalina將利用Digest解析server.xml(server的配置文件,server是最頂層容器),並根據server的配置文件建立server實例。
而後調用server實例的init()方法,進行server的初始化。
Server初始化時,又會調用其內部的Service的init方法(server只有一個,但service有多個)。
大致流程以下圖:
而後是Catlina中的start()方法:
Catlina的start()方法主要調用了server的start()方法來啓動服務器,而後server的start()方法再調用多個service的start()方法。
大致流程以下圖:
由上面Catlina的啓動咱們知道了,
Catlina中的load(args)方法使得Server進行了初始化,
Catlina中的start()方法使得Server也調用了start方法。
Server是一個接口,它的默認實現類是StandardServer,因此上面和以後咱們操做的Server其實是操做的StandardServer。
StandardServer繼承自LifecycleMBeanBase,而LifecycleMBeanBase又繼承自LifecycelBean。
根據以前的分析咱們知道,StandardServer主要要調用兩個方法:init()和start()。
而這兩個方法都不在StandardServer中,實際上都在StandardServer的父類LifecycelBean中。而LifecycelBean中的這兩個方法,實際上由「回頭」調用了StandardServer中的initInternal()和startInternal()方法。
也就是StandardServer調用了init()其實是調用了本身的initInternal()方法。
StandardServer調用了start()其實是調用了本身的startInternal()方法。
initInternal()方法和startInternal()方法「分別循環調用了每個service的init()方法和start()方法」。
除以上方法外,Standard還有addService()方法用來添加service,和remove()方法用來刪除service。
另外Standard還擁有await()方法,用法在上面Catlina的啓動中講了。
一個Serve中能夠有多個service,Service也是接口,Service接口的默認實現類是StandardService,因此上面和後面討論的service其實是在操做StandardService。
StandardService也繼承於LifecycleMBeanBase,而LifecycleMBeanBase繼承於LifecycelBean,LifecycelBean中存在init()和start()方法。
因此Server循環調用每一個StandardService的init()和start()方法,實際上是調用的StandardService的父類LifecycelBean中的init()和start()方法,而後init()和start()方法在「回頭調用」StandardService中的initInternal()方法和startInternal()方法
也就是說Server循環調用每一個StandardService的init()和start()方法,最後執行的是每一個Service的initInternal()方法和startInternal()方法。
每個service都是一個對外提供服務的組件,每個Service中都包含:一個Container+多個Connector,因此service中的方法,都是用來操做這兩種類的。
initInternal()方法:
下面是initInternal源碼:
protected void initInternal() throws LifecycleException { super.initInternal(); if(this.container != null) { this.container.init(); } Executor[] arr$ = this.findExecutors(); int arr$1 = arr$.length; int len$; for(len$ = 0; len$ < arr$1; ++len$) { Executor i$ = arr$[len$]; if(i$ instanceof JmxEnabled) { ((JmxEnabled)i$).setDomain(this.getDomain()); } i$.init(); } this.mapperListener.init(); Object var11 = this.connectorsLock; synchronized(this.connectorsLock) { Connector[] var12 = this.connectors; len$ = var12.length; for(int var13 = 0; var13 < len$; ++var13) { Connector connector = var12[var13]; try { connector.init(); } catch (Exception var9) { String message = sm.getString("standardService.connector.initFailed", new Object[]{connector}); log.error(message, var9); if(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) { throw new LifecycleException(message); } } } } }
首先,調用Container的init()方法,
再調用mapperListener的init()方法(mapperListener是用來監聽container的變化的),
再調用「多個」excutor的init()方法(excutor是用來在connector中管理線程池的),
再調用Connctor的init()方法。
startInternal()方法:
與上同理,也是分別調用四種類的start()方法。
總結一下,Server和Service的關係大體以下圖(須要說明的是,下圖中每一個類或接口中所包含的方法我並無全寫出來,只寫了與流程相關的幾個):
將到這裏應該提一下Lifecycle接口,該接口的默認實現就是LifecycleBean類。
經過上面提到Server和Service子類咱們發現他們都繼承了LifecycleBean類,並調用了該類的init()和start()方法。
實際上凡是擁有「生命週期」的組件都實現了Lifecycle接口,Lifecycle接口的做用就是進行「生命週期的規定」。
該接口主要作了如下四件事:
1、定義了13個String常量,這些常量被用於定義「LifecycleEvent事件」的type屬性中 。這樣設計的好處是,只須要發送一種類型事件LifecycleEvent,而根據裏面type屬性的值的不一樣,來斷定是什麼事件。(不然13種事件就得定義13中事件類)。
2、定義了三個管理監聽的方法,分別用來添加、查找、刪除LifecycleListener類型的監聽器。
3、定義了四個生命週期方法:init()、start()、stop()、destroy(),用於執行生命週期各個階段的操做。
4、定義了用來獲取當前狀態的兩個方法。
lifecycleBase是Lifecycle接口的默認實現類,全部擁有生命週期的組件都直接或間接的繼承自lifecycleBase類,lifecycleBase爲lifecycle接口中的各個方法提供了默認實現。
三個管理監聽的方法:
這三個方法分別是addLifecycleListener(添加監聽器)、findLifecycleListeners(查找監聽器)、removeLifecycleListener(刪除監聽器)。
監聽器的管理專門使用了一個「LifecycleSupport類」來完成。
這三個方法都調用了LifecycleSupport中的同名方法。
LifecycleSupport中經過一個數組來保存現有個監聽器。
另外LifecycleSupport中還定義了處理LifecycleEvent時間的方法。
四個生命週期方法:
主要就是init()和start()方法,在調用方法以前會先判斷當前狀態與要調用的方法是否匹配,若是不匹配則會執行相應的方法使其匹配(如在低啊用init以前先調用了start,這是就會先執行init)。
以後會調用相應的模板方法(initInternal()和startInternal())讓子類具體執行init和start。
咱們先看一下init()方法的源碼:
public final synchronized void init() throws LifecycleException { if(!this.state.equals(LifecycleState.NEW)) { this.invalidTransition("before_init"); } this.setStateInternal(LifecycleState.INITIALIZING, (Object)null, false); try { this.initInternal(); } catch (Throwable var2) { ExceptionUtils.handleThrowable(var2); this.setStateInternal(LifecycleState.FAILED, (Object)null, false); throw new LifecycleException(sm.getString("lifecycleBase.initFail", new Object[]{this.toString()}), var2); } this.setStateInternal(LifecycleState.INITIALIZED, (Object)null, false); }
能夠看出,init()方法先將當前狀態設置成了INITIALIZING,而後執行的是具體實現類的initInternal()方法。
再看看start()的源碼:
public final synchronized void start() throws LifecycleException { if(!LifecycleState.STARTING_PREP.equals(this.state) && !LifecycleState.STARTING.equals(this.state) && !LifecycleState.STARTED.equals(this.state)) { if(this.state.equals(LifecycleState.NEW)) { this.init(); } else if(this.state.equals(LifecycleState.FAILED)) { this.stop(); } else if(!this.state.equals(LifecycleState.INITIALIZED) && !this.state.equals(LifecycleState.STOPPED)) { this.invalidTransition("before_start"); } this.setStateInternal(LifecycleState.STARTING_PREP, (Object)null, false); try { this.startInternal(); } catch (Throwable var2) { ExceptionUtils.handleThrowable(var2); this.setStateInternal(LifecycleState.FAILED, (Object)null, false); throw new LifecycleException(sm.getString("lifecycleBase.startFail", new Object[]{this.toString()}), var2); } if(this.state.equals(LifecycleState.FAILED)) { this.stop(); } else if(!this.state.equals(LifecycleState.STARTING)) { this.invalidTransition("after_start"); } else { this.setStateInternal(LifecycleState.STARTED, (Object)null, false); } } else { if(log.isDebugEnabled()) { LifecycleException t = new LifecycleException(); log.debug(sm.getString("lifecycleBase.alreadyStarted", new Object[]{this.toString()}), t); } else if(log.isInfoEnabled()) { log.info(sm.getString("lifecycleBase.alreadyStarted", new Object[]{this.toString()})); } } }
由此可看到start方法判斷了當前狀態,若是狀態正確執行的起始是子類的startInternal()方法
兩個獲取當前狀態的方法:
在生命週期的相應方法中已經設置了state屬性,這兩個方法就是返回該屬性。
Connector的主要任務是負責處理瀏覽器發送過來的請求,並建立一個Request和Response的對象用於和瀏覽器交換數據,而後產生一個線程用於處理請求,Connector會把Request和Response對象傳遞給該線程,該線程的具體的處理過程是Container容器的事了。Container就是Servlet的容器,Container處理完後會吧結果返回給Connector,最後Connector使用socket將處理結果返回給客戶端,整個請求處理完畢。
Connector的底層是使用socket進行鏈接,Request和Response時按照http協議(協議可選)來封裝,因此Connector同時實現了TCP/IP和HTTP協議。
很明顯Connector應該能夠有多個,由於一次請求對應一個Connector,統一時間可能有多個請求。
Connector能夠處理不一樣協議類型的請求,默認處理的是HTTP1.1協議。
Connector中使用ProtocolHandler來具體處理請求,不一樣的ProtocolHandler表明不一樣協議的請求類型。
ProtocolHandler中又包含三個重要的組件:
Endpoint,用於處理底層socket網絡鏈接。
Processor,用於將socket接收到的socket封裝成request。
Adapter,用於將requst交給Container進行處理。
具體執行順序以下
1、以前講過了,Catlina會調用load方法,根據server.xml配置文件建立Server對象,以後Server由調用init()建立service,service再調用Connector的init方法建立並初始化。
因此Connector是在Catlina會調用load方法時建立的。
2、Conntctor首先執行的是初始化init,Conntctor的初始化主要是用來初始化ProtocolHandler。
因此Conntctor執行init後:
一、先建立一個Adapter,並設置到ProtocolHandler中(這個Adapter後面要用,也就是上面所提到的用於將requst交給Container進行處理。)
二、調用ProtocolHandler的init方法,讓它進行初始化。
三、調用mapperListener的init方法初始化(mapperListener做用是用來監聽容器,容器發生變化會被通知)
3、剛剛上面提到了ProtocolHandler的init方法被調用了,咱們在看看ProtocolHandler初始化都作了什麼。
整個ProtocolHandler結構以下圖:
ProtocolHandler的初始化方法被實現於「AbstractProtocol抽象類」中。
該抽象類有兩種子抽象類:AbstractAjpProtocol和AbstractHttp11Protocol,分別對應了兩種不一樣的請求協議。
因此最終咱們初始化的步驟實際上是在AbstractHttp11Protocol抽象類中執行(當協議爲HTTP時)。
配置文件server.xml中能夠指明須要建立的ProtocolHandler類型,默認是處理HTTP1.1協議的ProtocolHandler,以後咱們真正建立的就是AbstractHttp11Protocol抽象類的某一個子類(如Http11NioProtocol)
AbstractHttp11Protocol抽象類的初始化主要是調用了Endpoint的init初始化。
4、endpoint的init方法在抽象父類AbstractEndpoint中,是個模板方法,實際上調用的是子類NioEndpoint裏面的bind()方法。
這裏的bind()方法主要做用是,檢查Acceptor和poller的線程數量是否正確(必須大於等於1)。
提一下Acceptor和poller這兩個類,Acceptor的做用是控制與tomcat創建鏈接的數量,但Acceptor只負責創建鏈接。socket內容的讀寫是經過Poller來實現的。
此時Connector的init初始化就完成了。
5、Connector的init方法執行完後,會被調用執行startInternal方法。
同理,此方法會調用protocolHandler.start()方法。
而後protocolHandler的start又會調用endpoint.start()方法。
endpoint的start方法在抽象父類AbstractEndpoint中,是個模板方法,實際上調用的是子類NioEndpoint裏面的startInternal()方法。
該方法主要作了:
一、初始化一些屬性
二、根據以前定義的Acceptor和poller的線程數量,來啓動相應數量的Acceptor和poller。
其中poller會將請求交給SocketProcessor,而SocketProcessor的職責就是把具體的請求處理過程委派給Handler,handler會執行Processor接口裏的process()方法,進行「對request的解析,包括請求頭、請求行和請求體」。
而這個process()方法是在抽象父類AbstractHttp11Processor裏實現的,
process()方法會先從socket裏讀取http請求數據,並解析請求頭,構造Request對象和Response對象。
6、process()方法最後會調用Adapter的service()方法。
Adapter接口只有一個實現類CoyoteAdapter。
service()完成「請求行和請求體的解析」,並把解析出來的信息封裝到Request對象和Response對象中,以後service()便將封裝了Request以及Response對象的Socket傳給Container容器了。
傳輸的代碼是:
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
即:先從Connector中獲取service,而後從service中獲取Container。接着再獲取管道,再獲取管道的第一個值Value,最後調用invoke()方法執行請求。
七、調用mapperListener的start方法。,該方法註冊了已初始化的組件(在上面講的Connector的init時初始化的mapperListener),而後爲這些組件添加監聽器,最後添加容器之間的映射關係。
此時Connector的startIntenal就完成了。
至此整個Connector啓動完畢,大體的流程圖以下(須要說明的是,下圖中每一個類或接口中所包含的方法我並無全寫出來,只寫了與流程相關的幾個,而且多處地方的子類選擇不是惟一的,我畫的該流程是基於獲取HTTP1.1協議的請求):
Container容器是子容器的父接口,全部的子容器都繼承該接口。
Container容器還繼承Lifecycle接口,這樣就擁有了完整的生命週期,他的子容器也就擁有了完整的生命週期。
Container接口的實現子類爲ContainerBase,
其下的四個子容器接口爲:Engine、Host、Context、Wrapper。他們之間是逐層包含的,每一個service只能有一個Engine,一個Engine可以含有多個Host,每一個Host能夠含有多個Context,每一個Context可以含有多個Wrapper。
四個子容器接口也有各自的子實現類,這些實現類繼承自ContainerBase。
4中子容器的做用:
一、Engine:接收Connector傳來的request和response,選擇可用的Host來處理當前請求管理Host)。
二、Host:表明一個虛擬主機,也是一個站點。外面以www.demo.com/test這個url爲例,www.demo.com這個域名就表明一個Host,該域名對應的就是webapps目錄所表明的站點。
三、Context:表明一個應用,仍是以www.demo.com/test這個url爲例,經過域名能夠直接訪問到ROOT根目錄裏面的應用就是主應用。webapps/test目錄能夠經過www.demo.com/test訪問到,這個test就又是一個子應用。因而這就是兩個Context(每個應用對應一個Context)。
因此www.demo.com下的應用都屬於該Host站點。而其餘的域名又是其餘Host站點。
四、Wrapper:每個Wrapper封裝一個servlet。
這裏須要特殊說明的是Container的四個子容器的生命週期:
一、雖然四個子容器的共同父類ContainerBase定義了initInternal()和startInternal()方法,但子容器仍是能夠根據自身狀況再添加內容。
二、ContainerBase的init()方法是service初始化的時候調用的。但四個子容器的init方法「並非」在ContainerBase的init()方法中被循環調用。而是在執行start()方法時,經過狀態斷定來調用的init()(若是沒初始化就初始化)。
三、Context和Wrapper是「動態添加的」,在站點目錄下每放置一個war包,就會動態添加一個Context,在web.xml裏每配置一個servlet,就能夠動態添加一個Wrapper。因此子容器的start方法不只僅在「tomcat啓動的時候會被調用」,當"父容器ContainerBase添加某個子容器時",也會調用該子容器的start()方法。
Container的執行流程爲兩條:
第一條流程爲:Service執行init()調用Container的init()方法(注意,此時Container並不會調用子容器的init()方法),而後Service執行start()時調用Container的start,Container的start再循環調用每個子容器的start()方法。而後再調用管道的「生命週期管理」(管道不須要初始化,因此在init()中不會調用)。第一條流程的流程圖示以下:
Container的init()和start()方法執行內容以下:
1、Container的啓動是經過init()和start()完成的。以前分析過這兩個方法都會經過service來調用。
init()和start存在於LifecycelBean中,實際上最後調用的是ContainerBase中的initInternal()和startInternal()方法。
ContainerBase的initInternal方法主要是初始化了ThreadPoolExecutor類(startStopExecutor屬性),用於管理啓動和關閉的線程。
ThreadPoolExecutor繼承自Executor用於管理線程,這裏就不過多介紹了。
代碼以下:
protected void initInternal() throws LifecycleException { LinkedBlockingQueue startStopQueue = new LinkedBlockingQueue(); this.startStopExecutor = new ThreadPoolExecutor(this.getStartStopThreadsInternal(), this.getStartStopThreadsInternal(), 10L, TimeUnit.SECONDS, startStopQueue, new ContainerBase.StartStopThreadFactory(this.getName() + "-startStop-")); this.startStopExecutor.allowCoreThreadTimeOut(true); super.initInternal(); }
2、ContainerBase執行完initInternal後,會因爲service的start的調用,執行ContainerBase的startInternal()方法。
代碼以下:
protected synchronized void startInternal() throws LifecycleException { this.logger = null; this.getLogger(); //若是有Cluster和Realm,則調用他們的start方法。 Cluster cluster = this.getClusterInternal(); if(cluster != null && cluster instanceof Lifecycle) { ((Lifecycle)cluster).start(); } Realm realm = this.getRealmInternal(); if(realm != null && realm instanceof Lifecycle) { ((Lifecycle)realm).start(); } Container[] children = this.findChildren(); ArrayList results = new ArrayList(); //使用startStopExecutor調用新線程來啓動每個子容器 for(int fail = 0; fail < children.length; ++fail) { results.add(this.startStopExecutor.submit(new ContainerBase.StartChild(children[fail]))); } boolean var10 = false; Iterator i$ = results.iterator(); while(i$.hasNext()) { Future result = (Future)i$.next(); try { result.get(); } catch (Exception var9) { log.error(sm.getString("containerBase.threadedStartFailed"), var9); var10 = true; } } if(var10) { throw new LifecycleException(sm.getString("containerBase.threadedStartFailed")); } else { //調用管道的啓動方法 if(this.pipeline instanceof Lifecycle) { ((Lifecycle)this.pipeline).start(); } //設置生命週期狀態爲STARTING狀態。 this.setState(LifecycleState.STARTING); //調用threadStart方法啓動後臺線程 this.threadStart(); } }
startInternal()方法主要作了5件事:
一、若是有Cluster和Realm,則調用他們的start方法。
Cluster用於配置集羣,在server.xml中可配置,它的做用是同步session。
Realm是tomcat的安全域,能夠用來管理資源的訪問權限。
二、使用startStopExecutor調用新線程來啓動每個子容器(即調用他們的start()方法),具體啓動過程是經過一個for循環對每一個子容器啓動一個線程(這樣能夠多個線程同時啓動,效率高),並將返回的Future保存到一個List中,而後遍歷每一個Future並調用其get方法。(下面詳細說)
三、調用管道中Value的start方法來啓動管道。(下面詳細說)
四、設置生命週期狀態爲STARTING狀態。
五、調用threadStart方法啓動後臺線程,該線程是一個while循環,按期調用backgroundProcess方法作一些事情,間隔時間可經過屬性設置,單位是秒,若是小於0就不啓動後臺線程了。
backgroundProcess()方法在ContainerBase、StandardContext、StandardWrapper中都有實現。
ContainerBase中的backgroundProcess():調用了Cluster、Realm和管道的backgroundProcess()方法。
StandardContext中的backgroundProcess():調用ContainerBase中的backgroundProcess(),還對Session過時和資源變化進行了處理。
StandardWrapper中的backgroundProcess():調用ContainerBase中的backgroundProcess(),還會對jsp生成的servlet按期進行檢查。
至此Container的啓動就完成了,接下來咱們詳細的說一下上面的四個子容器的啓動,和管道的啓動。
先來看看使用startStopExecutor調用新線程來啓動每個子容器(即調用他們的start()方法)時,每個子容器都幹了些啥。
因爲每個子容器的接口都繼承自Container接口,而Container接口又繼承自Lifecycle接口,因此每個子容器都有完整的生命週期(都擁有start和init方法),因此調用各個子容器的start方法實際上就是調用他們各自實現類的startInternal()方法。
protected void initInternal() throws LifecycleException { this.getRealm(); super.initInternal(); }
由源碼可看出init方法調用了getRealm(),做用是若是沒有配置Realm,則使用默認的NullPealm。而後調用了父類ContainerBase中的initInternal方法。
protected synchronized void startInternal() throws LifecycleException { if(log.isInfoEnabled()) { log.info("Starting Servlet Engine: " + ServerInfo.getServerInfo()); } super.startInternal(); }
由源碼可看出:startInternal()只是調用了父類的startInternal()方法。
Host的默認實現類StandardHost沒有重寫initInternal方法,因此初始化時調用的是父類的相應方法。
startInternal代碼以下:
protected synchronized void startInternal() throws LifecycleException { String errorValve = this.getErrorReportValveClass(); if(errorValve != null && !errorValve.equals("")) { try { boolean t = false; Valve[] valves = this.getPipeline().getValves(); Valve[] valve = valves; int len$ = valves.length; for(int i$ = 0; i$ < len$; ++i$) { Valve valve1 = valve[i$]; if(errorValve.equals(valve1.getClass().getName())) { t = true; break; } } if(!t) { Valve var9 = (Valve)Class.forName(errorValve).newInstance(); this.getPipeline().addValve(var9); } } catch (Throwable var8) { ExceptionUtils.handleThrowable(var8); log.error(sm.getString("standardHost.invalidErrorReportValveClass", new Object[]{errorValve}), var8); } } super.startInternal(); }
由此看到此方法就是查看管道中是否有ErrorReportValve,若是沒有就將他添加進去。判斷的方法就是遍歷管道里面的全部value,而後經過每一個value的名字進行判斷。
在startInternal中經過一個var25的boolean來判斷是否進行下一步的處理,起始位true,當中途遇到問題後改成false,後面某些處理就不作了。
if(var25 && !this.listenerStart()) { log.error(sm.getString("standardContext.listenerFail")); var25 = false; }
此處觸發Listener(Listener定義在web.xml中)。
if(var25 && !this.filterStart()) { log.error(sm.getString("standardContext.filterFail")); var25 = false; }
此處觸發web.xml中配置的filter過濾器。
if(var25 && !this.loadOnStartup(this.findChildren())) { log.error(sm.getString("standardContext.servletFail")); var25 = false; }
此處初始化了全部Servlet,即調用在web.xml中配置了load-on-startup的servlet的init方法初始化(load-on-startup的做用是標記容器是否在啓動時加載此servlet)。
StandardWrapper沒有重寫initInternal方法,其startInternal()源碼以下:
protected synchronized void startInternal() throws LifecycleException { Notification notification; if(this.getObjectName() != null) { notification = new Notification("j2ee.state.starting", this.getObjectName(), (long)(this.sequenceNumber++)); this.broadcaster.sendNotification(notification); } super.startInternal(); this.setAvailable(0L); if(this.getObjectName() != null) { notification = new Notification("j2ee.state.running", this.getObjectName(), (long)(this.sequenceNumber++)); this.broadcaster.sendNotification(notification); } }
主要就作了三件事:
一、用broadcaster發送通知,只要用於JMX,
二、調用父類的startInternal方法
三、調用setAvailable方法,做用是設置Wrapper包含的全部servlet的有效的起始時間。
下面談論管道的startInternal方法:
由源碼可知管道是在ContainerBase中啓動的,而四個子容器都繼承了ContainerBase,因此四個自容器都有屬於本身的管道。
而每個管道里裝的都是「Value」類型,Value是接口,Value的實現類用於進行各類各樣的具體處理操做。
所謂管道就是指多個處理者(value)對某一個請求「依次」進行處理,請求交給第一個value處理完後,再交給第二個value處理·····,每一條管道的「最後一個Value」都會有點特殊,該Value會將請求發送給下一個管道,而後下一個管道拿到請求後,繼續交給該管道內的第一個value處理·····
舉例,如:
請求到達Engine後,其實是傳給了Engine的管道(由於Engine繼承了Container,因此他擁有StandardPipeline類型的管道),而後管道中有一個Value first屬性,該屬性是一個鏈式結構,存放在管道中first裏面的Value是該管道的第一個value,而該value內部存在指針,指向他的下一個value。
管道就能依靠頭value來遍歷該管道內的每個value,讓他們依次處理請求。
而Engine管道的最後一個value爲StandardEngineValue類,該value會將請求發送給Host的管道。
總結一下,每個子容器都有它的StandardPipeline管道,每一條管道中裝有多個value,value中的 invoke方法用來進行具體的處理請求,每一個管道中的最後一個value會將請求傳給下一個子容器的管道。(感受本身有點囉嗦了。。。 (`・ω・´))
回到管道的生命週期講解,
在ContainerBase中調用管道的啓動方法(該語句存在於startInternal中):
if(this.pipeline instanceof Lifecycle) { ((Lifecycle)this.pipeline).start(); }
Pipeline的實現類爲StandardPipeline類。該類中相應的源碼爲:
protected synchronized void startInternal() throws LifecycleException { Valve current = this.first; if(current == null) { current = this.basic; } for(; current != null; current = current.getNext()) { if(current instanceof Lifecycle) { ((Lifecycle)current).start(); } } this.setState(LifecycleState.STARTING); }
首先調用了first屬性賦值給了current,first屬性裏存儲的是「第一個value」,並將它賦值到了當前value上。
以後作了一個判斷,判斷當前value是否爲空,爲空就將當前value設置爲「最後一個value」。
以後從當前value(即:第一個value)開始調用start方法,每調用完一個,就將當前value的下一個value賦值到當前value上。即:循環調用該管道內全部value的start方法。
咱們再來看一下value的start方法。
value繼承於生命週期接口,因此調用start就是調用其子類的startInternal()方法。
全部的子value都繼承於ValueBase類,而startInternal方法就是定義在ValueBase類裏,源碼以下:
protected synchronized void startInternal() throws LifecycleException { this.setState(LifecycleState.STARTING); }
能夠看出ValueBase中的startInternal方法只作了一件事,就是將當前狀態設置爲STARTING。
至此,第一條流程就完成了。
第二條流程爲:Connector的Adapter將request和response傳給Container的管道,從Engine的管道一路處理到Wrapper的管道,Wrapper再將response一路返回給Engine,而後Engine將response返回給Connector,Connector再返回給用戶瀏覽器。
須要提早說明的是,四個自容器的管道中,每一個管道都有多個value用來依次處理請求,但我在這主要分析「每一個子容器管道的最後一個value」,分別是:StandardEngineValue、StandardHostValue、StandardContextValue、StandardWrapperValue。
final class StandardEngineValve extends ValveBase { private static final StringManager sm = StringManager.getManager("org.apache.catalina.core"); public StandardEngineValve() { super(true); } public final void invoke(Request request, Response response) throws IOException, ServletException { Host host = request.getHost(); if(host == null) { response.sendError(400, sm.getString("standardEngine.noHost", new Object[]{request.getServerName()})); } else { if(request.isAsyncSupported()) { request.setAsyncSupported(host.getPipeline().isAsyncSupported()); } host.getPipeline().getFirst().invoke(request, response); } } public final void event(Request request, Response response, CometEvent event) throws IOException, ServletException { request.getHost().getPipeline().getFirst().event(request, response, event); } }
這裏的invoke方法主要就作了一件事:
根據請求找到所對應的站點(Host),而後找到host的管道,調用其頭部value的invoke方法,並把request和response傳給他。
其invoke源碼爲:
public final void invoke(Request request, Response response) throws IOException, ServletException { Context context = request.getContext(); if(context == null) { response.sendError(500, sm.getString("standardHost.noContext")); } else { if(request.isAsyncSupported()) { request.setAsyncSupported(context.getPipeline().isAsyncSupported()); } boolean asyncAtStart = request.isAsync(); boolean asyncDispatching = request.isAsyncDispatching(); try { context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER); if(!asyncAtStart && !context.fireRequestInitEvent(request)) { return; } try { if(asyncAtStart && !asyncDispatching) { if(!response.isErrorReportRequired()) { throw new IllegalStateException(sm.getString("standardHost.asyncStateError")); } } else { context.getPipeline().getFirst().invoke(request, response); } } catch (Throwable var10) { ExceptionUtils.handleThrowable(var10); if(response.isErrorReportRequired()) { this.container.getLogger().error("Exception Processing " + request.getRequestURI(), var10); } else { request.setAttribute("javax.servlet.error.exception", var10); this.throwable(request, response, var10); } } response.setSuspended(false); Throwable t = (Throwable)request.getAttribute("javax.servlet.error.exception"); if(!context.getState().isAvailable()) { return; } if(response.isErrorReportRequired()) { if(t != null) { this.throwable(request, response, t); } else { this.status(request, response); } } if(!request.isAsync() && (!asyncAtStart || !response.isErrorReportRequired())) { context.fireRequestDestroyEvent(request); } } finally { if(ACCESS_SESSION) { request.getSession(false); } context.unbind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER); } } }
Host的用處再上上面已經講過了,一個host表明一個站點(也叫一個虛擬主機),
其處理過程能夠總結以下:
一、先根據request請求,選擇一個context容器。若是不爲空,則繼續處理。
二、調用Context容器的bind()方法。此方法獲取一條線程,而後讓該線程處理Context。
三、獲取context的管道,再調用管道中頭value的invoke方法,將request和response傳入其中。
其invoke源碼以下:
public final void invoke(Request request, Response response) throws IOException, ServletException { MessageBytes requestPathMB = request.getRequestPathMB(); if(!requestPathMB.startsWithIgnoreCase("/META-INF/", 0) && !requestPathMB.equalsIgnoreCase("/META-INF") && !requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0) && !requestPathMB.equalsIgnoreCase("/WEB-INF")) { Wrapper wrapper = request.getWrapper(); if(wrapper != null && !wrapper.isUnavailable()) { try { response.sendAcknowledgement(); } catch (IOException var6) { this.container.getLogger().error(sm.getString("standardContextValve.acknowledgeException"), var6); request.setAttribute("javax.servlet.error.exception", var6); response.sendError(500); return; } if(request.isAsyncSupported()) { request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported()); } wrapper.getPipeline().getFirst().invoke(request, response); } else { response.sendError(404); } } else { response.sendError(404); } }
其處理過程能夠總結以下:
一、先獲取request的請求路徑,該路徑不能直接訪問WEB-INF或者META-INF目錄下的資源。
二、再根據request,選擇合適的Wrapper。
三、再在response中設置一個sendAcknowledgement(在TCP/IP協議中,若是接收方成功的接收到數據,那麼會回覆一個ACK數據。)
四、獲取Wrapper的管道,再調用管道中頭value的invoke方法,將request和response傳入其中。
每一個Wrapper都封裝着一個servlet,因此Wrapper和servlet有着很深的聯繫。
StandardWrapper裏會加載他表明的servlet並建立實例(即調用它的init方法),但不會調用servlet的service方法。
StandardWrapperValue會調用sertvlet的service方法。
其具體執行順序以下,
一、分配一個Servlet實例,由於StandardWrapper負責加載servlet,因此也是從Wrapper中獲取servlet。
try { if(!unavailable) { servlet = wrapper.allocate(); } } catch (UnavailableException var38) { this.container.getLogger().error(sm.getString("standardWrapper.allocateException", new Object[]{wrapper.getName()}), var38); long requestPathMB = wrapper.getAvailable(); if(requestPathMB > 0L && requestPathMB < 9223372036854775807L) { response.setDateHeader("Retry-After", requestPathMB); response.sendError(503, sm.getString("standardWrapper.isUnavailable", new Object[]{wrapper.getName()})); } else if(requestPathMB == 9223372036854775807L) { response.sendError(404, sm.getString("standardWrapper.notFound", new Object[]{wrapper.getName()})); } } catch (ServletException var39) { this.container.getLogger().error(sm.getString("standardWrapper.allocateException", new Object[]{wrapper.getName()}), StandardWrapper.getRootCause(var39)); throwable = var39; this.exception(request, response, var39); } catch (Throwable var40) { ExceptionUtils.handleThrowable(var40); this.container.getLogger().error(sm.getString("standardWrapper.allocateException", new Object[]{wrapper.getName()}), var40); throwable = var40; this.exception(request, response, var40); servlet = null; }
二、執行Servlet相關的全部過濾器:
ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); try { if(servlet != null && filterChain != null) { if(context.getSwallowOutput()) { boolean var29 = false; try { var29 = true; SystemLogHandler.startCapture(); if(request.isAsyncDispatching()) { ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch(); var29 = false; } else if(comet1) { filterChain.doFilterEvent(request.getEvent()); var29 = false; } else { filterChain.doFilter(request.getRequest(), response.getResponse()); var29 = false; } } finally { if(var29) { String time = SystemLogHandler.stopCapture(); if(time != null && time.length() > 0) { context.getLogger().info(time); } } } String t2 = SystemLogHandler.stopCapture(); if(t2 != null && t2.length() > 0) { context.getLogger().info(t2); } } else if(request.isAsyncDispatching()) { ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch(); } else if(comet1) { filterChain.doFilterEvent(request.getEvent()); } else { filterChain.doFilter(request.getRequest(), response.getResponse()); } } }
ApplicationFilterChain能夠看作是一個「過濾器鏈」,StandardWrapperValve中的invoke會先建立該類的實例,而後調用它的doFilter方法,即從該鏈中的第一個過濾器開始調用。若是執行到了最後一個過濾器,就開始調用Servlet的service方法。
三、關閉過濾器鏈
if(filterChain != null) { if(request.isComet()) { filterChain.reuse(); } else { filterChain.release(); } }
四、通知Wrapper,從新委派處理完畢的servlet。
try { if(servlet != null) { wrapper.deallocate(servlet); } } catch (Throwable var31) { ExceptionUtils.handleThrowable(var31); this.container.getLogger().error(sm.getString("standardWrapper.deallocateException", new Object[]{wrapper.getName()}), var31); if(throwable == null) { throwable = var31; this.exception(request, response, var31); } }
至此Container第二條流程也完成了。
總結出來,簡易流程圖以下: