Tomcat 源碼分析(轉)

本文轉自:http://blog.csdn.net/haitao111313/article/category/1179996java

Tomcat源碼分析(一)--服務啓動

1. Tomcat主要有兩個組件,鏈接器和容器,所謂鏈接器就是一個http請求過來了,鏈接器負責接收這個請求,而後轉發給容器。容器即servlet容器,容器有不少層,分別是Engine,web

    Host,Context,Wrapper。最大的容器Engine,表明一個servlet引擎,接下來是Host,表明一個虛擬機,而後是Context,表明一個應用,Wrapper對應一個servlet。從鏈接器apache

    傳過來鏈接後,容器便會順序通過上面的容器,最後到達特定的servlet。要說明的是Engine,Host兩種容器在不是必須的。實際上一個簡單的tomcat只要鏈接器和容器就能夠了,編程

    但tomcat的實現爲了統一管理鏈接器和容器等組件,額外添加了服務器組件(server)和服務組件(service),添加這兩個東西的緣由我我的以爲就是爲了方便統一管理鏈接器和數組

    容器等各類組件。一個server能夠有多個service,一個service包含多個鏈接器和一個容器,固然還有一些其餘的東西,看下面的圖就很容易理解Tomcat的架構了:瀏覽器

    

2. 一個父組件又能夠包含多個子組件,這些被統一管理的組件都實現了Lifecycle接口。只要一個組件啓動了,那麼他的全部子組件也會跟着啓動,好比一個server啓動了,它的全部子緩存

    service都會跟着啓動,service啓動了,它的全部鏈接器和容器等子組件也跟着啓動了,這樣,tomcat要啓動,只要啓動server就好了,其餘的組件都會跟隨着啓動tomcat

3. 通常啓動Tomcat會是運行startup.bat或者startup.sh文件,實際上這兩個文件最後會調用org.apache.catalina.startup.Bootstrap類的main方法,這個main方法主要作了兩件事情,安全

    1:定義和初始化了tomcat本身的類加載器,2:經過反射調用了org.apache.catalina.startup.Catalina的process方法;服務器

4. process方法的功能也很簡單,1:若是catalina.home和catalina.base兩個屬性沒有設置就設置一下,2:參數正確的話就調用execute方法,execute的方法就是簡單的調用start方法,

    其中在判斷參數正確的方法arguments中會設置starting標識爲true,這樣在execute方法中就能調用start方法,start方法是重點,在它裏面啓動了咱們的Tomcat全部的服務

5. 這裏最重要的方法是createStartDigester();和((Lifecycle) server).start();createStartDigester方法主要的做用就是幫咱們實例化了全部的服務組件包括server,service和connect,至於

    怎麼實例化的等下再看,start方法就是啓動服務實例了。File file = configFile();是新建server.xml文件實例,後面的服務組件都是要根據這個文件來的

6. Digester是一個外部jar包裏面的類,主要的功能就是解析xml裏面的元素並把元素生成對象,把元素的屬性設置成對象的屬性,並造成對象間的父子兄弟等關係。

    Digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");//建立一個org.apache.catalina.core.StandardServer對象,實際上這裏並無真正

    建立出一個對象,而是添加一個模式,只是後面建立的對象是根據這些模式和server.xml來的,因此能夠暫時這麼理解。真正建立對象是在start方法裏面的digester.parse(is),is是

    server.xml文件的流,digester剛纔已經添加了StandardServer和StandardService等服務組件,也添加了StandardServer和StandardService的關係以及StandardService和鏈接器

    HttpConnector,容器StandardHost的關係,因此調用digester.parse(is)方法後就會根據模式和server.xml文件來生成對象以及他們之間的相互關係。這樣咱們便有了服務器組件

    StandardServer的對象,也有了它的子組件StandardService對象等等

7. 既然有了服務器組件的對象,就初始化而後啓動就能夠了,到此,tomcat就實現了啓動服務器組件StandardServer。啓動後作的事情就東西比較多,可是仍是比較清晰的,

    StandardServer的start方法關鍵代碼是啓動它的子組件StandardService。StandardService的start方法跟StandardServer的start方法差很少,是啓動它的鏈接器和容器,上面說了一個

    Service包含一個容器和多個鏈接器

8. 默認的鏈接器是HttpConnector,因此會調用HttpConnector的start方法。這裏有個兩個關鍵的類:HttpConnector和HttpProcessor,它們都實現了Runnable接口,HttpConnector

    負責接收http請求,HttpProcessor負責處理由HttpConnector接收到的請求。注意這裏HttpProcessor會有不少的實例,最大能夠有maxProcessor個,初始化是20個。因此在

    threadStart方法中會啓動一個後臺線程來接收http鏈接

9. 這樣,就會啓動HttpConnector後臺線程,它的run方法不斷循環,主要就是新建一個ServerSocket來監聽端口等待鏈接。serverSocket一直等待鏈接,獲得鏈接後給HttpProcessor

    的實例processor來處理,serverSocket則繼續循環監聽,至於processor具體怎麼處理,還有不少要說,這裏先不說。

 

Tomcat源碼分析(二)--鏈接處理

1.  在上一節裏已經啓動了一個HttpConnector線程,而且也啓動了固定數量的HttpProcessor線程。HttpConnector用來等待http鏈接,獲得http鏈接後交給其中的一個HttpProcessor

     線程來處理。接下里具體看一下HttpConnector是怎麼獲得鏈接得,以及HttpProcessor是怎麼處理的

2. 這裏很關鍵的就是socket = serverSocket.accept();和processor.assign(socket); 在循環裏面內,serverSocket.accept();負責接收http請求而後賦值給socket,最後交給其中一個processor

    處理。這裏processor並非等到須要的時候再實例化,而是在HttpConnector初始化的時候已經有了若干個processor。httpConnector裏面持有一個包含HttpProcessor對象的棧,須要的

    時候拿出來就是了。

3. 接下來由processor.assign(socket); 記住這個方法是異步的,不須要等待HttpProcessor來處理完成,因此HttpConnector才能不間斷的傳入Http請求

4. 很明顯,在它的run方法一開始即是調用上面的await方法來等待(由於一開始available變量爲false),因此HttpProcessor會一直阻塞,直到有線程來喚醒它。當從HttpConnector中調用

    processor.assign(socket),會把socket傳給此HttpProcessor對象,並設置available爲true,調用notifyAll()喚醒該processor線程以處理socket。同時,在await方法中又把available

    設置成false,所以又回到初始狀態,便可以從新接受socket。這裏處理socket的方法是process(socket),主要做用有兩點,1:解析這個socket,即解析http請求,包括請求方法,請求協議等,以

    填充request,response對象(是否是很熟悉,在servlet和jsp開發常常用到的request,response對象就是從這裏來的)。2:傳入request,response對象給和HttpConnector綁定的容器,讓容器來

    調用invoke方法進行處理。 

5. 在那些parse××方法裏面會對request,response對象進行初始化,而後調用容器的invoke方法進行處理,至此,http請求過來的鏈接已經完美的轉交給容器處理,容器剩下的問題就是要最終轉

    交給哪一個servlet或者jsp的問題。前面咱們知道,一個鏈接會跟一個容器相連,一個級別大的容器會有一個或者多個子容器,最小的容器是Wrapper,對應一個servlet,在這裏咱們只要知道請求的

    路徑決定了最終會選擇哪一個wrapper,wrapper最終會調用servlet的。至少一開始提出來的問題已經明白了。

 

Tomcat源碼分析(三)--鏈接器是如何與容器關聯的?

這篇文章要弄懂一個問題,咱們知道,一個連接器是跟一個容器關聯的,容器跟連接器是在何時關聯上的?

1. 在明白這個問題前要先了解一下Digester庫,這個庫簡單的說就是解析xml文件,這裏有兩個概念:模式和規則,所謂模式就是一個xml的標籤,規則就是遇到一個xml標籤須要作什麼,看一下他主要的三個方法:

    addObjectCreate(String pattern, String className, String attributeName)    根據模式pattern實例化一個對象className

    addSetProperties(String pattern)   設置這個模式的屬性

    addSetNext(String pattern, String methodName, String paramType)  添加模式之間的關係,調用父模式的

    上面可能很差理解,看tomcat是怎麼用到Digester的,在org.apache.catalina.startup.Catalina.createStartDigester()的方法裏,在這個方法裏有使用Digester來解析server.xml文件

2. 遇到標籤Server/Service/Connector的時候(這裏簡化了說法,應該是標籤Server下的子標籤Service的子標籤Connector,有點拗口),實例化HttpConnector,而後在它的上一級父容器StandardService

    下調用addConnector,這樣就把連接器HttpConnector添加進容器StandardService下了

3. 把一個連接器connector添加到StandardService的connectors數組裏,而後關聯上StandardService的容器

4. 當咱們調用了digester.addRuleSet(new EngineRuleSet("Server/Service/"));方法,Digester便會自動調用到EngineRuleSet類的addRuleInstances方法,在方法裏面無非也是添加各類模式和規則,

    根據上面的添加規則,很容易知道這裏又添加了一個StandardEngine對象(容器),而後又在該模式的上一級模式Server/Service添加StandardEngine跟StandardService的關係,即經過setContainer

    方法把容器添加進StandardService裏。

5. 把容器設置到StandardService下,在「同步代碼塊」處,把容器和連接器關聯上了,至此,容器和連接器就關聯上了。

 

Tomcat源碼分析(四)--容器處理連接之責任鏈模式

1. connector.getContainer()獲得的容器應該是StandardEngine(其實應該是由server.xml文件配置獲得的,這裏先假定是StandardEngine),StandardEngine沒有invoke方法,它繼承與

     ContainerBase(事實上全部的容器都繼承於ContainerBase,在ContainerBase類有一些容器的公用方法和屬性)

2. 由代碼可知ContainerBase的invoke方法是傳遞到Pipeline,調用了Pipeline的invoke方法。這裏要說一下Pipeline這個類,這是一個管道類,每個管道類Pipeline包含數個閥類,閥類是

    實現了Valve接口的類,Valve接口聲明瞭invoke方法。管道和閥的概念跟servlet編程裏面的過濾器機制很是像,管道就像過濾器鏈,閥就比如是過濾器。不過管道中還有一個基礎閥的概念,

    所謂基礎閥就是在管道中當管道把全部的普通閥都調用完成後再調用的。無論是普通閥仍是基礎閥,都實現了Value接口,也都繼承於抽象類ValveBase。在tomcat中,當調用了管道的

    invoke方法,管道則會順序調用它裏面的閥的invoke方法。

3. 其中StandardPipelineValveContext是管道里的一個內部類,內部類的做用是幫助管道順序調用閥Value的invoke方法

    內部類StandardPipelineValveContext的invokeNext方法經過使用局部變量來訪問下一個管道數組,管道類的變量stage保存當前訪問到第幾個閥,valves保存管道的全部閥,在調用普通閥的

    invoke方法是,會把內部類StandardPipelineValveContext自己傳進去,這樣在普通閥中就能調用invokeNext方法以便訪問下一個閥的invoke方法

4. 這個閥的invoke方法,經過傳進來到StandardPipelineValveContext(實現了ValveContext接口)的invokeNext方法來實現調用下一個閥的invoke方法。而後簡單的打印了請求的ip地址。

    最後再看StandardPipelineValveContext的invokeNext方法,調用完普通閥數組valves的閥後,開始調用基礎閥basic的invoke方法,這裏先說基礎閥的初始化,在每個容器的構造函數類就已經

    初始化了基礎閥。即在容器構造的時候就已經把基礎閥添加進管道pipeline中,這樣在StandardPipelineValveContext中的invokeNext方法裏就能調用基礎閥的invoke了,當

    basic.invoke(request, response, this);進入基礎閥StandardEngineValve

5. 在StandardEngine的基礎閥StandardEngineValve裏,調用了子容器invoke方法(這裏子容器就是StandardHost),還記得一開始connector.invoke(request, response)

    (即StandardEngine的invoke方法)如今順利的傳遞到子容器StandardHost的invoke方法,變成了StandardHost.invoke(request, response)。由此能夠猜想StandardHost也會傳遞給它的

    子容器,最後傳遞到最小的容器StandardWrapper的invoke方法,而後調用StandardWrapper的基礎閥StandardWrapperValue的invoke方法,因爲StandardWrapper是最小的容器了,

    不能再傳遞到其餘容器的invoke方法了,那它的invoke方法作了什麼?主要作了兩件事, 1:建立一個過濾器鏈並  2:分配一個servlet或者jsp

6. 這裏先不關注jsp,只關注一下servlet,經過servlet = wrapper.allocate(); 進入StandardWrapper的allocate方法,allocate主要就是調用了loadServlet方法,在

    loadServlet方法類用tomcat本身的類加載器實例化了一個servlet對象,並調用了該servlet的init和service方法。至此已經把請求傳遞到servlet的service(或者jsp的service)方法,

    整個處理請求到這裏就結束了,剩下的就是返回客戶端了。

 

Tomcat源碼分析(五)--容器處理鏈接之servlet的映射

本文所要解決的問題:一個http請求過來,容器是怎麼知道選擇哪一個具體servlet?

1. 一個Context容器表示一個web應用,一個Wrapper容器表示一個servlet,因此上面的問題能夠轉換爲怎麼由Context容器選擇servlet,答案是映射器。映射器是實現了Mapper接口的

    類,做用就是根據請求鏈接(主要是協議和路徑)來選擇下一個容器,能夠看作是一個哈希表,根據關鍵字段來選擇具體的值,Mapper接口

2. 在Tomcat源碼分析(四)--容器處理連接之責任鏈模式中已經知道,請求鏈接到達StandardContext容器的invoke方法,最終會到達StandardContextValue閥的invoke方法裏面,在這個

    invoke方法中有一句這樣的代碼。這句代碼表示容器會調用map方法來映射請求到具體的wrapper上,意思就是說,根據鏈接請求request來選擇wrapper。上面的map會調用父類ContainerBase

    的map方法來找到具體的映射器,至於這個映射器和容器是怎麼關聯上的,具體請參考 Tomcat源碼分析(三)--鏈接器是如何與容器關聯的?這篇文章,大體原理是同樣的。StandardContext容器

    有一個標準的映射器實現類StandardContextMapper,因此最終會調用到映射器StandardContextMapper的map方法,這個方法是選擇servlet的關鍵

3. 分4中匹配模式(徹底匹配,前綴匹配,擴展匹配,默認匹配)來選擇wrapper,關鍵代碼就是name = context.findServletMapping和wrapper = (Wrapper) context.findChild(name);這裏

    面context都是StandardContext。context.findServletMapping是根據匹配模式來找到servlet名字,context.findChild是根據servlet名字找到具體的wrapper。findServletMapping方法很

    簡單,就是在一個HashMap裏面獲得servlet名字

 

 

Tomcat源碼分析(六)--日誌記錄器和國際化

1. 只要實現Logger就能有一個本身的日誌記錄器,其中setContainer是把日誌記錄器跟具體的容器關聯,setVerbosity是設置日誌的級別,log是具體的日誌記錄函數。FATAL,ERROR,WARNING,

    INFORMATION,DEBUG表明日誌記錄的五個級別,看單詞就能明白意思。這裏主要講解一下FileLogger類,這是Tomcat的其中一個日誌記錄器,它把日誌記錄在一個文件中,FileLogger的啓動

    方法和關閉僅僅是出發一個生命週期事件,並不作其餘的事情

 

Tomcat源碼分析(七)--單一啓動/關閉機制(生命週期)

1. Tomcat有不少組件,要一個一個啓動組件不免有點麻煩。因爲Tomcat的包含關係是Catalina->Server->Service->容器/鏈接器/日誌器等,因而可經過父組件負責啓動/關閉它的子組件,

    這樣只要啓動Catalina,其餘的都自動啓動了。這種單一啓動和關閉的機制是經過實現Lifecycle接口來實現的

2. 當組件實現了Lifecycle接口,父組件啓動的時候,即調用start方法時,只要在父組件的start方法中也調用子組件的start方法便可(只有實現統一的接口Lifecycle才能實現統一調用,如如下調用

    方式:(Lifecycle)子組件.start())

3. 關鍵看((Lifecycle) server).start();這樣便在啓動Catalina的時候啓動了Server,再看StandardServer的start方法

    主要作了兩件事,1:發送生命週期事件給監聽者;2:啓動子組件services(至於server怎麼關聯上services請看前面的幾篇文章,之後都再也不題怎麼關聯上的了)。

4. 這裏先岔開一下,說一下監聽器,lifecycle是一個工具類LifecycleSupport的實例,每個組件都有這樣一個工具類,這個工具類的做用就是幫助管理該組件上的監聽器,包括添加監聽器和羣發

    事件給監聽器

5. 先看構造方法,傳入一個lifecycle,由於每一個組件都實現了lifecycle,因此這裏傳入的其實是一個組件,即每一個組件都有一個LifecycleSupport與之關聯,當要在組件中添加一個監聽器的時候,

    其實是添加進工具類LifecycleSupport的一個監聽器數組listeners中,當要發送一個組件生命週期的事件時,工具類就會遍歷監聽器數組,而後再一個一個的發送事件。這裏須要先實現咱們

    本身的監聽器類而且添加進咱們須要監聽的組件當中。實現監聽器類只要實現LifecycleListener接口就行

6. 咱們須要作的就是實現LifecycleListener接口來擁有本身的監聽器,在lifecycleEvent方法裏寫本身監聽到事件後該作的事情,而後添加進要監聽的組件就行,好比當咱們要看StandardServer

    是否啓動了,在上面StandardServer的start方法有一句這樣的代碼:lifecycle.fireLifecycleEvent(START_EVENT, null);即發送StandardServer啓動的事件給跟它關聯的監聽器。接下來回

    到一開始,當server啓動後,接着啓動它的子組件service,即調用StandardService的start方法,這個方法跟StandardServer的start方法差很少,只是啓動了鏈接器和容器,鏈接器的start

    方法在前面的文章已經講過了,主要是啓動了n個處理器HttpProcessor組件。頂級容器是StandardEngine,它的start方法僅僅調用了父類ContainerBase的start方法

    它啓動了Tomcat其餘全部的組件,包括加載器,映射器,日誌記錄器,管道等等,由這裏也能夠看出,他們都實現了Lifecycle接口。統一關閉跟統一啓動的邏輯差很少,這裏就再也不說了。

 

Tomcat源碼分析(八)--載入器(加載器)

1. Tomcat就是用的這種方法。jdk建議咱們實現本身的類加載器的時候是重寫findClass方法,不建議重寫loadclass方法,由於ClassLoader的loadclass方法保證了類加載的父親委託機制,

    若是重寫了這個方法,就意味着須要實現本身在重寫的loadclass方法實現父親委託機制

2. 當全部父類都加載不了,纔會調用findClass方法,即調用到咱們本身的類加載器的findClass方法

3. 在咱們本身實現的類加載器中,defineClass方法纔是真正的把類加載進jvm,defineClass是從ClassLoader繼承而來,把一個表示類的字節數組加載進jvm轉換爲一個類。

4. 咱們本身實現的類加載器跟系統的類加載器沒有本質的區別,最大的區別就是加載的路徑不一樣,系統類加載器會加載環境變量CLASSPATH中指明的路徑和jvr文件,咱們本身的類加載器

    能夠定義本身的須要加載的類文件路徑.一樣的一個class文件,用系統類加載器和自定義加載器加載進jvm後類的結構是沒有區別的,只是他們訪問的權限不同,生成的對象由於加載器不一樣

    也會不同.固然咱們本身的類加載器能夠有更大的靈活性,好比把一個class文件(其實就是二進制文件)加密後(簡單的加密就把0和1互換),系統類加載器就不能加載,須要由咱們本身定義

    解密類的加載器才能加載該class文件.

5. 如今來初步的看看Tomcat的類加載器,爲何Tomcat要有本身的類加載器.這麼說吧,假如沒有本身的類加載器,咱們知道,在一個Tomcat中是能夠部署不少應用的,若是全部的類都由

    系統類加載器來加載,那麼部署在Tomcat上的A應用就能夠訪問B應用的類,這樣A應用與B應用之間就沒有安全性可言了。還有一個緣由是由於Tomcat須要實現類的自動重載,因此也

    須要實現本身的類加載器。Tomcat的載入器是實現了Loader接口的WebappLoader類,也是Tomcat的一個組件,實現Lifecycle接口以便統一管理,啓動時調用start方法,在start

    方法主要作了如下的事情:

    1:建立一個真正的類加載器以及設置它加載的路徑,調用createClassLoader方法

    2:啓動一個線程來支持自動重載,調用threadStart方法

6. 在createClassLoader方法內,實例化了一個真正的類加載器WebappClassLoader,代碼很簡單。WebappClassLoader是Tomcat的類加載器,它繼承了

    URLClassLoader(這個類是ClassLoader的孫子類)類。重寫了findClass和loadClass方法。Tomcat的findClass方法並無加載相關的類,只是從已經加載的類中查找

    這個類有沒有被加載,具體的加載是在重寫的loadClass方法中實現,從上面的對java的討論可知,重寫了loadClass方法就意味着失去了類加載器的父親委託機制,須要本身

    來實現父親委託機制。

7. 上面的代碼很長,但實現了Tomcat本身的類加載機制,具體的加載規則是:

    1:由於全部已經載入的類都會緩存起來,因此先檢查本地緩存

    2:如本地緩存沒有,則檢查上一級緩存,即調用ClassLoader類的findLoadedClass()方法;

    3:若兩個緩存都沒有,則使用系統的類進行加載,防止Web應用程序中的類覆蓋J2EE的類

    4:若打開標誌位delegate(表示是否代理給父加載器加載),或者待載入的類是屬於包觸發器的包名,則調用父類載入器來加載,若是父類載入器是null,則使用系統類載入器

    5:從當前倉庫中載入相關類

    6:若當前倉庫中沒有相關類,且標誌位delegate爲false,則調用父類載入器來加載,若是父類載入器是null,則使用系統類載入器(4跟6只能執行一個步驟的)

8. 先檢查系統ClassLoader,所以WEB-INF/lib和WEB-INF/classes或{tomcat}/libs下的類定義不能覆蓋JVM 底層可以查找到的定義(譬如不能經過定義java.lang.Integer替代底層的實現  

 

Tomcat源碼分析(九)--Session管理

1. JSESSIONID是一個惟一標識號,用來標識服務器端的Session,也用來標識客戶端的Cookie,客戶端和服務器端經過這個JSESSIONID來一一對應。這裏須要說明的是Cookie已經包含

    JSESSIONID了,能夠理解爲JSESSIONID是Cookie裏的一個屬性。

2. 讓我假設一次客戶端鏈接來講明我對個這三個概念的理解:

    Http鏈接自己是無狀態的,即前一次發起的鏈接跟後一次沒有任何關係,是屬於兩次獨立的鏈接請求,可是互聯網訪問基本上都是須要有狀態的,即服務器須要知道兩次鏈接請求是否是同

    一我的訪問的。如你在瀏覽淘寶的時候,把一個東西加入購物車,再點開另外一個商品頁面的時候但願在這個頁面裏面的購物車還有上次添加進購物車的商品。也就是說淘寶服務器會知道這

    兩次訪問是同一個客戶端訪問的。

    客戶端第一次請求到服務器鏈接,這個鏈接是沒有附帶任何東西的,沒有Cookie,沒有JSESSIONID。服務器端接收到請求後,會檢查此次請求有沒有傳過來JSESSIONID或者Cookie,

    若是沒有JSESSIONID和Cookie,則服務器端會建立一個Session,並生成一個與該Session相關聯的JSESSIONID返回給客戶端,客戶端會保存這個JSESSIONID,並生成一個與該

    JSESSIONID關聯的Cookie,第二次請求的時候,會把該Cookie(包含JSESSIONID)一塊兒發送給服務器端,此次服務器發現這個請求有了Cookie,便從中取出JSESSIONID,而後

    根據這個JSESSIONID找到對應的Session,這樣便把Http的無狀態鏈接變成了有狀態的鏈接。可是有時候瀏覽器(即客戶端)會禁用Cookie,咱們知道Cookie是經過Http的請求頭部

    的一個cookie字段傳過去的,若是禁用,那麼便得不到這個值,JSESSIONID便不能經過Cookie傳入服務器端,固然咱們還有其餘的解決辦法,url重寫和隱藏表單,url重寫就是把

    JSESSIONID附帶在url後面傳過去。隱藏表單是在表單提交的時候傳入一個隱藏字段JSESSIONID。這兩種方式都能把JSESSIONID傳過去。

3. 代碼主要就是從http請求頭部的字段cookie獲得JSESSIONID並設置到reqeust的sessionid,沒有就不設置。這樣客戶端的JSESSIONID(cookie)就傳到tomcat,tomcat

    把JSESSIONID的值賦給request了。這個request在Tomcat的惟一性就標識了。

4. Session只對應用有用,兩個應用的Session通常不能共用,在Tomcat一個Context表明一個應用,因此一個應用應該有一套本身的Session,Tomcat使用Manager來管理各個應用

    的Session,Manager也是一個組件,跟Context是一一對應的關係,怎麼關聯的請參考Tomcat源碼分析(一)--服務啓動,方法相似。Manager的標準實現是StandardManager,

    由它統一管理Context的Session對象(標準實現是StandardSession),可以猜測,StandardManager必定可以建立Session對象和根據JSESSIONID從跟它關聯的應用中查找

    Session對象。事實上StandardManager確實有這樣的方法,可是StandardManager自己沒有這兩個方法

5. Session是在何時生成的?仔細想一想,咱們編寫servlet的時候,若是須要用到Session,會使用request.getSession(),這個方法最後會調用到HttpRequestBase的

    getSession()方法,因此這裏有個重要的點:Session並非在客戶端第一次訪問就會在服務器端生成,而是在服務器端(通常是servlet裏)使用request調用getSession方法才

     生成的。可是默認狀況下,jsp頁面會調用request.getSession(),即jsp頁面的這個屬性<%@ page session="true" %>默認是true的,編譯成servlet後會調用

     request.getSession()。因此只要訪問jsp頁面,通常是會在服務器端建立session的。可是在servlet裏就須要顯示的調用getSession(),固然是在要用session的狀況。

 

Tomcat源碼分析(十)--部署器

1. 在Tomcat的世界裏,一個Host容器表明一個虛機器資源,Context容器表明一個應用,所謂的部署器就是可以把Context容器添加進Host容器中去的一個組件。顯然,一個Host

    容器應該擁有一個部署器組件。

2. 在Catalina的createStartDigester()方法中,向StandardHost容器中添加了一個HostConfig的實例。HostConfig類實現了LifecycleListener接口,也就是說它是個監聽器類,

    能監聽到組件的生命週期事件

3. 若是監聽到StandardHost容器啓動開始了,則調用start方法來

4. ((Deployer) host).install(contextPath, url);會調用到StandardHost的install方法,再由StandardHost轉交給StandardHostDeployer的install方法,

    StandardHostDeployer是一個輔助類,幫助StandardHost來實現發佈應用,它實現了Deployer接口,看它的install(URL config, URL war)方法(它有兩個install方法,分別

    用來發布上面不一樣方式的應用

相關文章
相關標籤/搜索