TOMCAT源碼分析(啓動框架)

 前言:  css

    本文是我閱讀了TOMCAT源碼後的一些心得。 主要是講解TOMCAT的系統框架, 以及啓動流程。如有錯漏之處,敬請批評指教!  html

 建議:  java

    畢竟TOMCAT的框架仍是比較複雜的, 單是從文字上理解, 是不那麼容易掌握TOMCAT的框架的。 因此得實踐、實踐、再實踐。 建議下載一份TOMCAT的源碼, 調試經過, 而後單步跟蹤其啓動過程。 若是有不明白的地方, 再來查閱本文, 看是否能獲得幫助。 我相信這樣效果以及學習速度都會好不少!  web

1. Tomcat的總體框架結構  數據庫

    Tomcat的基本框架, 分爲4個層次。  apache

    Top Level Elements:  瀏覽器

     Server  tomcat

     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的服務, 同時也提供給咱們解析靜態文本的服務。  

    Connector: Tomcat都是在容器裏面處理問題的, 而容器又到哪裏去取得輸入信息呢?  

 Connector就是專幹這個的。 他會把從socket傳遞過來的數據, 封裝成Request, 傳遞給容器來處理。  

    一般咱們會用到兩種Connector,一種叫http connectoer, 用來傳遞http需求的。 另外一種叫AJP, 在咱們整合apache與tomcat工做的時候,apache與tomcat之間就是經過這個協議來互動的。 (說到apache與tomcat的整合工做, 一般咱們的目的是爲了讓apache 獲取靜態資源, 而讓tomcat來解析動態的jsp或者servlet。)  

    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的框架,它是一種自上而下, 容器裏又包含子容器的這樣一種結構。  

2. Tomcat的啓動流程  

    這篇文章是講tomcat怎麼啓動的,既然咱們大致上瞭解了TOMCAT的框架結構了, 那麼咱們能夠望文生意地就猜到tomcat的啓動, 會先啓動父容器,而後逐個啓動裏面的子容器。 啓動每個容器的時候, 都會啓動安插在他身上的組件。 當全部的組件啓動完畢, 全部的容器啓動完畢的時候,tomcat自己也就啓動完畢了。  

    瓜熟蒂落地, 咱們一樣能夠猜到,tomcat的啓動會分紅兩大部分, 第一步是裝配工做。 第二步是啓動工做。  

    裝配工做就是爲父容器裝上子容器, 爲各個容器安插進組件的工做。 這個地方咱們會用到digester模式, 至於digester模式什麼, 有什麼用, 怎麼工做的. 請參考<http://software.ccidnet.com/pub/article/c322_a31671_p2.html>;  

    啓動工做是在裝配工做以後, 一旦裝配成功了, 咱們就只須要點燃最上面的一根導線, 整個tomcat就會被激活起來。 這就比如咱們要開一輛已經裝配好了的汽車的時候同樣,咱們只要把鑰匙插進鑰匙孔,一擰,汽車的引擎就會發動起來,空調就會開起來, 安全裝置就會生效, 如此一來,汽車整個就發動起來了。(這個過程確實和TOMCAT的啓動過程不謀而和, 讓咱們不得不懷疑TOMCAT的設計者是在GE作JAVA開發的)。  

2.1 一些有意思的名稱:  

    Catalina  

    Tomcat  

    Bootstrap  

    Engin  

    Host  

    Context  

    他們的意思頗有意思:  

    Catalina: 遠程轟炸機  

    Tomcat: 熊貓轟炸機-- 轟炸機的一種  

    Bootstap: 引導  

    Engin: 發動機  

    Host: 主機,領土  

    Context: 內容, 目標, 上下文  

    綜上所述, 這又不得不讓人聯想到GE是否是也參與了軍事設備的生產呢?  

2.2  歷史就是那麼驚人的類似!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方法, 並傳遞參數過去。  

2.3 Catalina.java  

    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。  

2.4 啓動各個容器  

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終於不辭辛勞地爲人民服務了!  

3. 參考文獻:  

     <http://jakarta.apache.org/tomcat/>;  

     <http://www.onjava.com/pub/a/onjava/2003/05/14/java_webserver.html>;  

4. 後記  

     這篇文章是講解tomcat啓動框架的,還有篇文章是講解TOMCAT裏面的消息處理流程的細節的。 文章內容已經寫好了, 如今正在整理階段。 相信很快就能夠作出來, 你們共同研究共同進步。  

     這篇文章是獨自分析TOMCAT源碼所寫的, 因此必定有地方是帶有我的主觀色彩, 不免會有片面之處。如有不當之處敬請批評指教,這樣不只可使剛開始研究TOMCAT的兄弟們少走彎路, 我也能夠學到東西。  

5. tomcat源碼分析(消息處理)  

 [ZT]TOMCAT源碼分析  

0:前言  

 咱們知道了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>; 

相關文章
相關標籤/搜索