TOMCAT原始碼分析(啟動框架)

Michael_DD發表於2014-12-11
TOMCAT原始碼分析(啟動框架)



前言:
   本文是我閱讀了TOMCAT原始碼後的一些心得。 主要是講解TOMCAT的系統框架, 以及啟動流程。若有錯漏之處,敬請批評指教!

建議:
   畢竟TOMCAT的框架還是比較複雜的, 單是從文字上理解, 是不那麼容易掌握TOMCAT的框架的。 所以得實踐、實踐、再實踐。
   建議下載一份TOMCAT的原始碼, 除錯透過, 然後單步跟蹤其啟動過程。 如果有不明白的地方, 再來查閱本文,
   看是否能得到幫助。 我相信這樣效果以及學習速度都會好很多!
   
1. Tomcat的整體框架結構
   Tomcat的基本框架, 分為4個層次。
   Top Level Elements:
    Server
    Service   
   Connector
    HTTP
    AJP
   Container
   Engine
     Host
   Context
   Component  
    manager
   logger
   loader
   pipeline
   valve
         ...
   站在框架的頂層的是Server和Service
   
   Server:  其實就是BackGroud程式, 在Tomcat裡面的Server的用處是啟動和監聽服務端事件(諸如重啟、關閉等命令。
   在tomcat的標準配置檔案:server.xml裡面, 我們可以看到“;”
   這裡的"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來處理。
   比如:<
         <
   前者交給foo這個Context來處理, 後者交給bar這個Context來處理。
   很明顯吧!context的意思其實就是一個web app的意思。
   我們通常都會在server.xml裡面做這樣的配置
   ;
   這個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模式什麼, 有什麼用, 怎麼工作的. 請參考<

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

2.1 一些有意思的名稱:
   Catalina
   Tomcat
   Bootstrap
   Engin
   Host
   Context
   他們的意思很有意思:
   Catalina: 遠端轟炸機
   Tomcat: 熊貓轟炸機-- 轟炸機的一種(這讓我想起了讓國人引以為豪的熊貓手機,是不是英文可以叫做tomcat??? ,
   又讓我想起了另一則廣告: 波導-手機中的戰鬥機、波音-客機中的戰鬥機 )
   Bootstap: 引導
   Engin: 發動機
   Host: 主機,領土
   Context: 內容, 目標, 上下文
   
   ... 在許多許多年後, 現代人類已經滅絕。 後現代生物發現了這些單詞零落零落在一塊。 一個自以為聰明的傢伙把這些東西翻譯出來了:

   在地勤人員的引導(bootstrap)下, 一架轟炸架(catalina)騰空躍起, 遠看是熊貓轟炸機(tomcat),
   近看還是熊貓轟炸機! 憑藉著優秀的發動機技術(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 啟動帶有;1;的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. 參考文獻:
    <
    <
   
4. 後記
    這篇文章是講解tomcat啟動框架的,還有篇文章是講解TOMCAT裡面的訊息處理流程的細節的。 文章內容已經寫好了, 現在正在整理階段。
    相信很快就可以做出來, 大家共同研究共同進步。
    這篇文章是獨自分析TOMCAT原始碼所寫的, 所以一定有地方是帶有個人主觀色彩, 難免會有片面之處。若有不當之處敬請批評指教,
    這樣不僅可以使剛開始研究TOMCAT的兄弟們少走彎路, 我也可以學到東西。
 
5. tomcat原始碼分析(訊息處理)
 
[ZT]TOMCAT原始碼分析

0:前言
我們知道了tomcat的整體框架了, 也明白了裡面都有些什麼元件, 以及各個元件是幹什麼用的了。
 

 
我想,接下來我們應該去了解一下tomcat 是如何處理jsp和servlet請求的。
 
 
 
1.  我們以一個具體的例子,來跟蹤TOMCAT, 看看它是如何把Request一層一層地遞交給下一個容器, 並最後交給Wrapper來處理的。
 
以為例子
 
(以下例子, 都是以tomcat4 原始碼為參考)
 
 
 
這篇心得主要分為3個部分: 前期, 中期, 和末期。
 
前期:講解了在瀏覽器裡面輸入一個URL,是怎麼被tomcat抓住的。
 
中期:講解了被tomcat抓住後,又是怎麼在各個容器裡面穿梭, 最後到達最後的處理地點。
 
末期:講解到達最後的處理地點後,又是怎麼具體處理的。

 
2、  前期Request的born.
 
    在這裡我先簡單講一下request這個東西。
 
     我們先看著這個URL:  它是動用了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()方法這裡設定一個斷點, 然後訪問
 
    , 我們來看看在前期都會路過哪些地方:
 
       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, 比如
 
這裡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!
 
參考:
 
     <
   <

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29500582/viewspace-1364589/,如需轉載,請註明出處,否則將追究法律責任。

相關文章