前言:css
本文是我閱讀了TOMCAT源碼後的一些心得。 主要是講解TOMCAT的系統框架, 以及啓動流程。如有錯漏之處,敬請批評指教!html
建議:java
畢竟TOMCAT的框架仍是比較複雜的, 單是從文字上理解, 是不那麼容易掌握TOMCAT的框架的。 因此得實踐、實踐、再實踐。 建議下載一份TOMCAT的源碼, 調試經過, 而後單步跟蹤其啓動過程。 若是有不明白的地方, 再來查閱本文, 看是否能獲得幫助。 我相信這樣效果以及學習速度都會好不少!
web
Tomcat的基本框架, 分爲4個層次。
Server和Service
Connector
HTTP
AJP
Container
Engine
Host
Context
Component
manager
logger
loader
pipeline
valve
...
站在框架的頂層的是Server和Service數據庫
Server: 其實就是BackGroud程序, 在Tomcat裏面的Server的用處是啓動和監聽服務端事件(諸如重啓、關閉等命令。 在tomcat的標準配置文件:server.xml裏面, 咱們能夠看到「<Server port="8005" shutdown="SHUTDOWN" debug="0">;」這裏的"SHUTDOWN"就是server在監聽服務端事件的時候所使用的命令字)
Service: 在tomcat裏面,service是指一類問題的解決方案。 一般咱們會默認使用tomcat提供的:Tomcat-Standalone 模式的service。 在這種方式下的service既給咱們提供解析jsp和servlet的服務, 同時也提供給咱們解析靜態文本的服務。
Connectorapache
Tomcat都是在容器裏面處理問題的, 而容器又到哪裏去取得輸入信息呢?
Connector就是專幹這個的。 他會把從socket傳遞過來的數據, 封裝成Request, 傳遞給容器來處理。
一般咱們會用到兩種Connector,一種叫http connectoer, 用來傳遞http需求的。 另外一種叫AJP, 在咱們整合apache與tomcat工做的時候,apache與tomcat之間就是經過這個協議來互動的。 (說到apache與tomcat的整合工做, 一般咱們的目的是爲了讓apache 獲取靜態資源, 而讓tomcat來解析動態的jsp或者servlet。)bootstrap
Container瀏覽器
當http connector把需求傳遞給頂級的container---Engin的時候, 咱們的視線就應該移動到Container這個層面來了。
在Container這個層, 咱們包含了3種容器:Engin, Host, Context.
Engin:收到service傳遞過來的需求, 處理後, 將結果返回給service( service 是經過connector 這個媒介來和Engin互動的).
Host: Engin收到service傳遞過來的需求後,不會本身處理, 而是交給合適的Host來處理。Host在這裏就是虛擬主機的意思, 一般咱們都只會使用一個主機,既「localhost」本地機來處理。
Context: Host接到了從Host傳過來的需求後, 也不會本身處理, 而是交給合適的Context來處理。
好比:<http://127.0.0.1:8080/foo/index.jsp>;
<http://127.0.1:8080/bar/index.jsp>;
前者交給foo這個Context來處理, 後者交給bar這個Context來處理。很明顯吧!context的意思其實就是一個web app的意思。
咱們一般都會在server.xml裏面作這樣的配置<Context path="/foo" docBase="D:/project/foo/web" />;這個context容器,就是用來幹咱們該乾的事兒的地方的。
Compenent: 接下來, 咱們繼續講講component是幹什麼用的。
咱們得先理解一下容器和組件的關係。
需求被傳遞到了容器裏面, 在合適的時候, 會傳遞給下一個容器處理。而容器裏面又盛裝着各類各樣的組件, 咱們能夠理解爲提供各類各樣的增值服務。
manager: 當一個容器裏面裝了manager組件後,這個容器就支持session管理了, 事實上在tomcat裏面的session管理, 就是靠的在context裏面裝的manager component.
logger: 當一個容器裏面裝了logger組件後, 這個容器裏所發生的事情, 就被該組件記錄下來啦! 咱們一般會在logs/ 這個目錄下看見catalina_log.time.txt 以及 localhost.time.txt 和localhost_examples_log.time.txt。 這就是由於咱們分別爲:engin, host以及context(examples)這三個容器安裝了logger組件, 這也是默認安裝, 又叫作標配 :)
loader: loader這個組件一般只會給咱們的context容器使用,loader是用來啓動context以及管理這個context的classloader用的。
pipline: pipeline是這樣一個東西, 當一個容器決定了要把從上級傳遞過來的需求交給子容器的時候, 他就把這個需求放進容器的管道(pipeline)裏面去。 而需求傻呼呼得在管道里面流動的時候, 就會被管道里面的各個閥門攔截下來。 好比管道里面放了兩個閥門。 第一個閥門叫作「access_allow_vavle」, 也就是說需求流過來的時候,它會看這個需求是哪一個IP過來的, 若是這個IP已經在黑名單裏面了,sure, 殺! 第二個閥門叫作「defaul_access_valve」它會作例行的檢查, 若是經過的話,OK, 把需求傳遞給當前容器的子容器。 就是經過這種方式, 需求就在各個容器裏面傳遞,流動, 最後抵達目的地的了。
valve: 就是上面所說的閥門啦。
Tomcat裏面大概就是這麼些東西, 咱們能夠簡單地這麼理解tomcat的框架,它是一種自上而下, 容器裏又包含子容器的這樣一種結構。tomcat
這篇文章是講tomcat怎麼啓動的,既然咱們大致上瞭解了TOMCAT的框架結構了, 那麼咱們能夠望文生意地就猜到tomcat的啓動, 會先啓動父容器,而後逐個啓動裏面的子容器。 啓動每個容器的時候, 都會啓動安插在他身上的組件。 當全部的組件啓動完畢, 全部的容器啓動完畢的時候,tomcat自己也就啓動完畢了。
瓜熟蒂落地, 咱們一樣能夠猜到,tomcat的啓動會分紅兩大部分, 第一步是裝配工做。 第二步是啓動工做。
裝配工做就是爲父容器裝上子容器, 爲各個容器安插進組件的工做。 這個地方咱們會用到digester模式, 至於digester模式什麼, 有什麼用, 怎麼工做的. 請參考<http://software.ccidnet.com/pub/article/c322_a31671_p2.html>;
啓動工做是在裝配工做以後, 一旦裝配成功了, 咱們就只須要點燃最上面的一根導線, 整個tomcat就會被激活起來。 這就比如咱們要開一輛已經裝配好了的汽車的時候同樣,咱們只要把鑰匙插進鑰匙孔,一擰,汽車的引擎就會發動起來,空調就會開起來, 安全裝置就會生效, 如此一來,汽車整個就發動起來了。(這個過程確實和TOMCAT的啓動過程不謀而和, 讓咱們不得不懷疑TOMCAT的設計者是在GE作JAVA開發的)。安全
Catalina
Tomcat
Bootstrap
Engin
Host
Context
他們的意思頗有意思:
Catalina: 遠程轟炸機
Tomcat: 熊貓轟炸機-- 轟炸機的一種(這讓我想起了讓國人引覺得豪的熊貓手機,是否是英文能夠叫作tomcat??? , 又讓我想起了另外一則廣告: 波導-手機中的戰鬥機、波音-客機中的戰鬥機 )
Bootstap: 引導
Engin: 發動機
Host: 主機,領土
Context: 內容, 目標, 上下文
... 在許多許多年後, 現代人類已經滅絕。 後現代生物發現了這些單詞零落零落在一塊。 一個自覺得聰明的傢伙把這些東西翻譯出來了:
在地勤人員的引導(bootstrap)下, 一架轟炸架(catalina)騰空躍起, 遠看是熊貓轟炸機(tomcat), 近看仍是熊貓轟炸機! 憑藉着優秀的發動機技術(engin), 這架熊貓轟炸機飛臨了敵國的領土上空(host), 對準目標(context)投下了毀天滅地的核彈頭,波~ 現代生物就這麼隔屁了~
綜上所述, 這又不得不讓人聯想到GE是否是也參與了軍事設備的生產呢?
反對美帝國主義! 反對美霸權主義! 和平萬歲! 自由萬歲!
tomcat的啓動就是從org.apache.catalina.startup.Bootstrap這個類悍然啓動的!
在Bootstrap裏作了兩件事:
1. 指定了3種類型classloader:
commonLoader: common/classes、common/lib、common/endorsed
catalinaLoader: server/classes、server/lib、commonLoader
sharedLoader: shared/classes、shared/lib、commonLoader
2. 引導Catalina的啓動。
用Reflection技術調用org.apache.catalina.startup.Catalina的process方法, 並傳遞參數過去。
Catalina完成了幾個重要的任務:
1. 使用Digester技術裝配tomcat各個容器與組件。
1.1 裝配工做的主要內容是安裝各個大件。 好比server下有什麼樣的servcie。Host會容納多少個context。Context都會使用到哪些組件等等。
1.2 同時呢, 在裝配工做這一步, 還完成了mbeans的配置工做。 在這裏,我簡單地但不十分精確地描述一下mbean是什麼,幹什麼用的。
咱們本身生成的對象, 本身管理, 天經地義! 可是若是咱們建立了對象了, 想讓別人來管, 怎麼辦呢? 我想至少得告訴別人咱們都有什麼, 以及經過什麼方法能夠找到 吧!JMX技術給咱們提供了一種手段。JMX裏面主要有3種東西。Mbean, agent, connector.
Mbean: 用來映射咱們的對象。也許mbean就是咱們建立的對象, 也許不是, 但有了它, 就能夠引用到咱們的對象了。
Agent: 經過它, 就能夠找到mbean了。
Connector: 鏈接Agent的方式。 能夠是http的, 也能夠是rmi的,還能夠直接經過socket。
發生在tomcat 裝配過程當中的事情: GlobalResourcesLifecycleListener 類的初始化會被觸發:
protected static Registry registry = MBeanUtils.createRegistry(); 會運行
MBeanUtils.createRegistry() 會依據/org/apache/catalina/mbeans/mbeans- descriptors.xml這個配置文件建立mbeans. Ok, 外界就有了條途徑訪問tomcat中的各個組件了。(有點像後門兒)
2. 爲top level 的server 作初始化工做。 實際上就是作一般會配置給service的兩條connector.(http, ajp)
3. 從server這個容器開始啓動, 點燃整個tomcat.
4. 爲server作一個hook程序, 檢測當server shutdown的時候, 關閉tomcat的各個容器用。
5. 監聽8005端口, 若是發送"SHUTDOWN"(默認培植下字符串)過來, 關閉8005serverSocket。
1. Server
觸發Server容器啓動前(before_start), 啓動中(start), 啓動後(after_start)3個事件, 並運行相應的事件處理器。
啓動Server的子容器:Servcie.
2. Service
啓動Service的子容器:Engin
啓動Connector
3. Engin
到了Engin這個層次,以及如下級別的容器,Tomcat就使用了比較一致的啓動方式了。
首先, 運行各個容器本身特有一些任務
隨後, 觸發啓動前事件
當即, 設置標籤,就表示該容器已經啓動
接着, 啓動容器中的各個組件:loader, logger, manager等等
再接着,啓動mapping組件。(注1)
緊跟着,啓動子容器。
接下來,啓動該容器的管道(pipline)
而後, 觸發啓動中事件
最後, 觸發啓動後事件。
Engin大體會這麼作,Host大體也會這麼作, Context大體仍是會這麼作。 那麼很顯然地, 咱們須要在這裏使用到代碼複用的技術。tomcat在處理這個問題的時候, 漂亮地使用了抽象類來處理。ContainerBase. 最後使得這部分完成複雜功能的代碼顯得乾淨利落, 幹練爽快, 實在是使人以爲歎爲觀止, 細細品來, 直覺如享佳珍, 另人齒頰留香, 留戀往返啊!
Engin的觸發啓動前事件裏, 會激活綁定在Engin上的惟一一個Listener:EnginConfig。
這個EnginConfig類基本上沒有作什麼事情, 就是把EnginConfig的調試級別設置爲和Engin至關。 另外就是輸出幾行文本, 表示Engin已經配置完畢, 並無作什麼實質性的工做。
注1: mapping組件的用處是, 當一個需求將要從父容器傳遞到子容器的時候, 而父容器又有多個子容器的話, 那麼應該選擇哪一個子容器來處理需求呢? 這個由mapping 組件來定奪。
4. Host
同Engin同樣, 也是調用ContainerBase裏面的start()方法, 不過以前作了些自個兒的任務,就是往Host這個容器的通道(pipline)裏面, 安裝了一個叫作
「org.apache.catalina.valves.ErrorReportValve」的閥門。
這個閥門的用處是這樣的: 需求在被Engin傳遞給Host後, 會繼續傳遞給Context作具體的處理。 這裏需求其實就是做爲參數傳遞的Request, Response。 因此在context把需求處理完後, 一般會改動response。 而這個org.apache.catalina.valves.ErrorReportValve的做用就是檢察response是否包含錯誤, 若是有就作相應的處理。
5. Context
到了這裏, 就終於輪到了tomcat啓動中真正的重頭戲,啓動Context了。
StandardContext.start() 這個啓動Context容器的方法被StandardHost調用.
5.1 webappResources 該context所指向的具體目錄
5.2 安裝defaultContex, DefaultContext 就是默認Context。 若是咱們在一個Host下面安裝了DefaultContext,並且defaultContext裏面又安裝了一個數據庫鏈接池資源的話。 那麼其餘全部的在該Host下的Context, 均可以直接使用這個數據庫鏈接池, 而不用格外作配置了。
5.3 指定Loader. 一般用默認的org.apache.catalina.loader.WebappLoader這個類。 Loader就是用來指定這個context會用到哪些類啊, 哪些jar包啊這些什麼的。
5.4 指定Manager. 一般使用默認的org.apache.catalina.session. StandardManager 。Manager是用來管理session的。
其實session的管理也很好實現。 以一種簡單的session管理爲例。 當需求傳遞過來的時候, 在Request對象裏面有一個sessionId 屬性。OK, 獲得這個sessionId後, 咱們就能夠把它做爲map的key,而value咱們能夠放置一個HashMap. HashMap裏邊兒, 再放咱們想放的東西。
5.5 postWorkDirectory (). Tomcat下面有一個work目錄。 咱們把臨時文件都扔在那兒去。 這個步驟就是在那裏建立一個目錄。 通常說來會在%CATALINA_HOME%/work/Standalone\localhost\ 這個地方生成一個目錄。
5.6 Binding thread。到了這裏, 就應該發生class Loader 互換了。 以前是看得見tomcat下面全部的class和lib. 接下來須要看得見當前context下的class。 因此要設置contextClassLoader, 同時還要把舊的ClassLoader記錄下來,由於之後還要用的。
5.7 啓動Loader. 指定這個Context具體要使用哪些classes, 用到哪些jar文件。 若是reloadable設置成了true, 就會啓動一個線程來監視classes的變化, 若是有變化就從新啓動Context。
5.8 啓動logger
5.9 觸發安裝在它身上的一個監聽器。
lifecycle.fireLifecycleEvent(START_EVENT, null);
做爲監聽器之一,ContextConfig會被啓動. ContextConfig就是用來配置web.xml的。 好比這個Context有多少Servlet, 又有多少Filter, 就是在這裏給Context裝上去的。
5.9.1 defaultConfig. 每一個context都得配置tomcat/conf/web.xml 這個文件。
5.9.2 applicationConfig 配置本身的WEB-INF/web.xml 文件
5.9.3 validateSecurityRoles 權限驗證。 一般咱們在訪問/admin 或者/manager的時候,須要用戶要麼是admin的要麼是manager的, 才能訪問。 並且咱們還能夠限制那些資源能夠訪問, 而哪些不能。 都是在這裏實現的。
5.9.4 tldScan: 掃描一下, 須要用到哪些標籤(tag lab)
5.10 啓動manager
5.11 postWelcomeFiles() 咱們一般會用到的3個啓動文件的名稱:
index.html、index.htm、index.jsp 就被默認地綁在了這個context上
5.12 listenerStart 配置listener
5.13 filterStart 配置filter
5.14 啓動帶有<load-on-startup>;1</load-on-startup>;的Servlet.
順序是從小到大:1,2,3… 最後是0
默認狀況下, 至少會啓動以下3個的Servlet:
org.apache.catalina.servlets.DefaultServlet
處理靜態資源的Servlet. 什麼圖片啊,html啊,css啊,js啊都找他
org.apache.catalina.servlets.InvokerServlet
處理沒有作Servlet Mapping的那些Servlet.
org.apache.jasper.servlet.JspServlet
處理JSP文件的.
5.15 標識context已經啓動完畢。
走了多少個步驟啊,Context總算是啓動完畢嘍。
OK! 走到了這裏, 每一個容器以及組件都啓動完畢。Tomcat終於不辭辛勞地爲人民服務了!
<http://jakarta.apache.org/tomcat/>;
<http://www.onjava.com/pub/a/onjava/2003/05/14/java_webserver.html>;
咱們知道了tomcat的總體框架了, 也明白了裏面都有些什麼組件, 以及各個組件是幹什麼用的了。 http://www.csdn.net/Develop/read_article.asp?id=27225 我想,接下來咱們應該去了解一下tomcat 是如何處理jsp和servlet請求的。 1. 咱們以一個具體的例子,來跟蹤TOMCAT, 看看它是如何把Request一層一層地遞交給下一個容器, 並最後交給Wrapper來處理的。 以http://localhost:8080/web/login.jsp爲例子 (如下例子, 都是以tomcat4 源碼爲參考) 這篇心得主要分爲3個部分: 前期, 中期, 和末期。 前期:講解了在瀏覽器裏面輸入一個URL,是怎麼被tomcat抓住的。 中期:講解了被tomcat抓住後,又是怎麼在各個容器裏面穿梭, 最後到達最後的處理地點。 末期:講解到達最後的處理地點後,又是怎麼具體處理的。 二、 前期Request的born. 在這裏我先簡單講一下request這個東西。 咱們先看着這個URL:http://localhost:8080/web/login.jsp 它是動用了8080端口來進行socket通信的。 咱們知道, 經過 InputStream in = socket.getInputStream() 和OutputStream out = socket.getOutputStream() 就能夠實現消息的來來每每了。 可是若是把Stream給應用層看,顯然操做起來不方便。 因此,在tomcat 的Connector裏面,socket被封裝成了Request和Response這兩個對象。 咱們能夠簡單地把Request當作管發到服務器來的數據,把Response當作想發出服務器的數據。 可是這樣又有其餘問題了啊?Request這個對象是把socket封裝起來了, 可是他提供的東西太多了。 諸如Request.getAuthorization(), Request.getSocket()。 像Authorization這種東西開發人員拿來基本上用不太着,而像socket這種東西,暴露給開發 人員又有潛在的危險。 並且啊, 在Servlet Specification裏面標準的通訊類是ServletRequest和HttpServletRequest,而非這個Request類。So, So, So. Tomcat必須得搗持搗持Request才行。 最後tomcat選擇了使用搗持模式(應該叫適配器模式)來解決這個問題。它把org.apache.catalina.Request 搗持成了org.apache.coyote.tomcat4.CoyoteRequest。 而CoyoteRequest又實現了ServletRequest和HttpServletRequest 這兩種接口。 這樣就提供給開發人員須要且剛剛須要的方法了。 ok, 讓咱們在tomcat的頂層容器- StandardEngin 的invoke()方法這裏設置一個斷點, 而後訪問 http://localhost:8080/web/login.jsp , 咱們來看看在前期都會路過哪些地方: 1. run(): 536, java.lang.Thread, Thread.java CurrentThread 2. run():666, org.apache.tomcat.util.threads.ThreadPool$ControlRunnable, ThreadPool.java ThreadPool 3. runIt():589, org.apache.tomcat.util.net.TcpWorkerThread, PoolTcpEndpoint.java ThreadWorker 4. processConnection(): 549,org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler, Http11Protocol.java http protocol parser 5. Process(): 781, org.apache.coyote.http11.Http11Processor, Http11Processor.java http request processor 6. service(): 193, org.apache.coyote.tomcat4.CoyoteAdapter,CoyoteAdapter.java adapter 7. invoke(): 995, org.apache.catalina.core.ContainerBase, ContainerBase.java StandardEngin 1. 主線程 2. 啓動線程池. 3. 調出線程池裏面空閒的工做線程。 4. 把8080端口傳過來由httpd協議封裝的數據,解析成Request和Response對象。 5. 使用Http11Processor來處理request 6. 在Http11Processor裏面, 又會call CoyoteAdapter來進行適配處理,把Request適配成實現了ServletRequest和HttpServletRequest接口的CoyoteRequest. 7. 到了這裏,前期的去毛拔皮工做就基本上搞定,能夠交給StandardEngin 作核心的處理工做了。 3. 中期。 在各個容器間的穿梭。 Request在各個容器裏面的穿梭大體是這樣一種方式: 每一個容器裏面都有一個管道(pipline), 專門用來傳送Request用的。 管道里面又有好幾個閥門(valve), 專門用來過濾Request用的。 在管道的低部一般都會放上一個默認的閥們。 這個閥們至少會作一件事情,就是把Request交給子容器。 讓咱們來想象一下: 當一個Request進入一個容器後, 它就在管道里面流動,波羅~ 波羅~ 波羅~ 地穿過各個閥門。在流到最後一個閥門的時候,吧唧~ 那個該死的閥門就把它扔給了子容器。 而後又開始 波羅~ 波羅~ 波羅~ ... 吧唧~.... 波羅~ 波羅~ 波羅~ ....吧唧~.... 就是經過這種方式,Request 走完了全部的容器。( 感受有點像消化系統,最後一個地方有點像那裏~ ) OK, 讓咱們具體看看都有些什麼容器, 各個容器裏面又都有些什麼閥門,這些閥們都對咱們的Request作了些什麼吧: 3.1 StandardEngin 的pipeline裏面放的是:StandardEnginValve 在這裏,VALVE作了三件事: 1. 驗證傳遞過來的request是否是httpservletRequest. 2 驗證傳遞過來的request 是否攜帶了host header信息. 3 選擇相應的host去處理它。(通常咱們都只有一個host:localhost,也就是127.0.0.1)。 到了這個地方, 咱們的request就已經完成了在Engin這個部分的歷史使命, 通向前途未卜的下一站:host了。 3.2 StandardHost 的pipline裏面放的是:StandardHostValve 1. 驗證傳遞過來的request是否是httpservletRequest. 2. 根據Request來肯定哪一個Context來處理。 Context其實就是webapp, 好比http://localhost:8080/web/login.jsp 這裏web就是Context羅! 3. 既然肯定了是哪一個Context了,那麼就應該把那個Context的classloader付給當前線程了。 Thread.currentThread().setContextClassLoader(context.getLoader().getClassLoader()); 這樣request就只看得見指定的context下面的classes啊,jar啊這些, 而看不見tomcat自己的類, 什麼Engin啊,Valve啊。 否則還得了啊! 4. 既然request到了這裏了,看來用戶是準備訪問web這個web app了,咋們得更新一下這個用戶的session不是!Ok , 就由manager更新一下用戶的session信息 5. 交給具體的Context 容器去繼續處理Request. 6. Context處理完畢了,把classloader還回來。 3.3 StandardContext 的pipline裏面放的是:StandardContextValve 1. 驗證傳遞過來的request是否是httpservletRequest. 2. 若是request意圖不軌,想要訪問/meta-inf, /web-inf這些目錄下的東西,呵呵,沒有用D! 3. 這個時候就會根據Request究竟是Servlet,仍是jsp,仍是靜態資源來決定到底用哪一種Wrapper來處理這個Reqeust了。 4. 一旦決定了到底用哪一種Wrapper,OK,交給那個Wrapper處理。 4. 末期。 不一樣的需求是怎麼處理的. StandardWrapper 以前對Wrapper沒有作過講解,其實它是這樣一種東西。 咱們在處理Request的時候,能夠分紅3種。 處理靜態的:org.apache.catalina.servlets.DefaultServlet 處理jsp的:org.apache.jasper.servlet.JspServlet 處理servlet的:org.apache.catalina.servlets.InvokerServlet 不一樣的request就用這3種不一樣的servlet去處理。 Wrapper就是對它們的一種簡單的封裝,有了Wrapper後,咱們就能夠輕鬆地攔截每次的Request。也能夠容易地調用servlet的init()和destroy()方法, 便於管理嘛! 具體狀況是這麼滴: 若是request是找jsp文件,StandardWrapper裏面就會封裝一個org.apache.jasper.servlet.JspServlet去處理它。 若是request是找 靜態資源 ,StandardWrapper裏面就會封裝一個org.apache.jasper.servlet.DefaultServlet 去處理它。 若是request是找servlet ,StandardWrapper裏面就會封裝一個org.apache.jasper.servlet.InvokerServlet 去處理它。 StandardWrapper一樣也是容器,既然是容器, 那麼裏面必定留了一個管道給request去穿,管道低部確定也有一個閥門(注1),用來作最後一道攔截工做. 在這最底部的閥門裏,其實就主要作了兩件事: 一是啓動過濾器,讓request在N個過濾器裏面篩一通,若是OK! 那就PASS。 不然就跳到其餘地方去了。 二是servlet.service((HttpServletRequest) request,(HttpServletResponse) response); 這個方法. 若是是JspServlet, 那麼先把jsp文件編譯成servlet_xxx, 再invoke servlet_xxx的servie()方法。 若是是DefaultServlet, 就直接找到靜態資源,取出內容, 發送出去。 若是是InvokerServlet, 就調用那個具體的servlet的service()方法。 ok! 完畢。 注1: StandardWrapper 裏面的閥門是最後一道關口了。 若是這個閥門欲意把request交給StandardWrapper 的子容器處理。 對不起, 在設計考慮的時候,Wrapper就被考慮成最末的一個容器, 壓根兒就不會給Wrapper添加子容器的機會! 若是硬是要調用addChild(), 立馬拋出IllegalArgumentException! 參考: <http://jakarta.apache.org/tomcat/>; <http://www.onjava.com/pub/a/onjava/2003/05/14/java_webserver.html>;