一、請求過程
Tomca的兩大組件:Connecter和Container
Connecter組件
1、Connecter將在某個指定的端口上偵聽客戶請求,接收瀏覽器的發過來的 tcp 連接請求,創建一個 Request 和 Response 對象分別用於和請求端交換數據,然后會產生一個線程來處理這個請求並把產生的 Request 和 Response 對象傳給處理Engine(Container中的一部分),從Engine出獲得響應並返回客戶。
Tomcat中有兩個經典的Connector,一個直接偵聽來自Browser的HTTP請求,另外一個來自其他的WebServer請求。Cotote HTTP/1.1 Connector在端口8080處偵聽來自客戶Browser的HTTP請求,Coyote JK2 Connector在端口8009處偵聽其他Web Server的Servlet/JSP請求。
Connector 最重要的功能就是接收連接請求然后分配線程讓 Container 來處理這個請求,所以這必然是多線程的,多線程的處理是 Connector 設計的核心。
Container組件
2、Container是容器的父接口,該容器的設計用的是典型的責任鏈的設計模式,它由四個自容器組件構成,分別是Engine、Host、Context、Wrapper。這四個組件是負責關系,存在包含關系。通常一個Servlet class對應一個Wrapper,如果有多個Servlet定義多個Wrapper,如果有多個Wrapper就要定義一個更高的Container,如Context。
Context 還可以定義在父容器 Host 中,Host 不是必須的,但是要運行 war 程序,就必須要 Host,因為 war 中必有 web.xml 文件,這個文件的解析就需要 Host 了,如果要有多個 Host 就要定義一個 top 容器 Engine 了。而 Engine 沒有父容器了,一個 Engine 代表一個完整的 Servlet 引擎。
及其他容器:
- Engine 容器
Engine 容器比較簡單,它只定義了一些基本的關聯關系 - Host 容器
Host 是 Engine 的字容器,一個 Host 在 Engine 中代表一個虛擬主機,這個虛擬主機的作用就是運行多個應用,它負責安裝和展開這些應用,並且標識這個應用以便能夠區分它們。它的子容器通常是 Context,它除了關聯子容器外,還有就是保存一個主機應該有的信息。 - Context 容器
Context 代表 Servlet 的 Context,它具備了 Servlet 運行的基本環境,理論上只要有 Context 就能運行 Servlet 了。簡單的 Tomcat 可以沒有 Engine 和 Host。Context 最重要的功能就是管理它里面的 Servlet 實例,Servlet 實例在 Context 中是以 Wrapper 出現的,還有一點就是 Context 如何才能找到正確的 Servlet 來執行它呢? Tomcat5 以前是通過一個 Mapper 類來管理的,Tomcat5 以后這個功能被移到了 request 中,在前面的時序圖中就可以發現獲取子容器都是通過 request 來分配的。 - Wrapper 容器
Wrapper 代表一個 Servlet,它負責管理一個 Servlet,包括的 Servlet 的裝載、初始化、執行以及資源回收。Wrapper 是最底層的容器,它沒有子容器了,所以調用它的 addChild 將會報錯。
Wrapper 的實現類是 StandardWrapper,StandardWrapper 還實現了擁有一個 Servlet 初始化信息的 ServletConfig,由此看出 StandardWrapper 將直接和 Servlet 的各種信息打交道。
Tomcat Server處理一個HTTP請求的過程
1、用戶點擊網頁內容,請求被發送到本機端口8080,被在那里監聽的Coyote HTTP/1.1 Connector獲得。
2、Connector把該請求交給它所在的Service的Engine來處理,並等待Engine的回應。
3、Engine獲得請求localhost/test/index.jsp,匹配所有的虛擬主機Host。
4、Engine匹配到名為localhost的Host(即使匹配不到也把請求交給該Host處理,因為該Host被定義為該Engine的默認主機),名為localhost的Host獲得請求/test/index.jsp,匹配它所擁有的所有的Context。Host匹配到路徑為/test的Context(如果匹配不到就把該請求交給路徑名為“ ”的Context去處理)。
5、path=“/test”的Context獲得請求/index.jsp,在它的mapping table中尋找出對應的Servlet。Context匹配到URL PATTERN為*.jsp的Servlet,對應於JspServlet類。
6、構造HttpServletRequest對象和HttpServletResponse對象,作為參數調用JspServlet的doGet()或doPost().執行業務邏輯、數據存儲等程序。
7、Context把執行完之后的HttpServletResponse對象返回給Host。
8、Host把HttpServletResponse對象返回給Engine。
9、Engine把HttpServletResponse對象返回Connector。
10、Connector把HttpServletResponse對象返回給客戶Browser。
二、類加載器流程
在tomcat中類的加載稍有不同,如下圖:
當tomcat啟動時,會創建幾種類加載器:
1 Bootstrap 引導類加載器
加載JVM啟動所需的類,以及標准擴展類(位於jre/lib/ext下)
2 System 系統類加載器
加載tomcat啟動的類,比如bootstrap.jar,通常在catalina.bat或者catalina.sh中指定。位於CATALINA_HOME/bin下。
3 Common 通用類加載器
加載tomcat使用以及應用通用的一些類,位於CATALINA_HOME/lib下,比如servlet-api.jar
4 webapp 應用類加載器
每個應用在部署后,都會創建一個唯一的類加載器。該類加載器會加載位於 WEB-INF/lib下的jar文件中的class 和 WEB-INF/classes下的class文件。
當應用需要到某個類時,則會按照下面的順序進行類加載:
1 使用bootstrap引導類加載器加載
2 使用system系統類加載器加載
3 使用應用類加載器在WEB-INF/classes中加載
4 使用應用類加載器在WEB-INF/lib中加載
5 使用common類加載器在CATALINA_HOME/lib中加載
問題擴展
通過對上面tomcat類加載機制的理解,就不難明白 為什么java文件放在Eclipse中的src文件夾下會優先jar包中的class?
這是因為Eclipse中的src文件夾中的文件java以及webContent中的JSP都會在tomcat啟動時,被編譯成class文件放在 WEB-INF/class 中。
而Eclipse外部引用的jar包,則相當於放在 WEB-INF/lib 中。
因此肯定是 java文件或者JSP文件編譯出的class優先加載。
通過這樣,我們就可以簡單的把java文件放置在src文件夾中,通過對該java文件的修改以及調試,便於學習擁有源碼java文件、卻沒有打包成xxx-source的jar包。
另外呢,開發者也會因為粗心而犯下面的錯誤。
在 CATALINA_HOME/lib 以及 WEB-INF/lib 中放置了 不同版本的jar包,此時就會導致某些情況下報加載不到類的錯誤。
還有如果多個應用使用同一jar包文件,當放置了多份,就可能導致 多個應用間 出現類加載不到的錯誤。