天天看點

深入了解Tomcat系列之七:詳解URL請求

前言

這裡分析一個實際的請求是如何在tomcat中被處理的,以及最後是怎麼樣找到要處理的servlet的?當我們在浏覽器中輸入<code>http://hostname:port/contextpath/servletpath</code>,前面的hostname與port用于建立tcp連接配接,由于http也是基于tcp協定的,是以這裡涉及tcp連接配接的三次握手。後面的contextpath與servletpath則是與伺服器進行請求的資訊,contextpath指明了與伺服器中哪個context容器進行互動,伺服器會根據這個url與對應的context容器建立連接配接,那麼這個過程是如何實作的呢?

在tomcat7(本文也是基于tomcat7)中主要通過一個映射來完成的,這個映射的工作交給org.apache.tomcat.util.http.mapper.mapper類來完成的,這個類儲存了container容器所有子容器的資訊,在請求從connector交給container容器之前,mapper會根據hostname和port将host容器與context容器設定到request的mappingdata屬性中,這樣在connector的請求進入container容器之前就知道了交給哪個容器了。

這段代碼如下:

代碼清單5-4:

下面啟動伺服器,在浏覽器中輸入http://localhost:8080/examples/jsp/jsp2/el/composite.jsp,斷點調試可以mappingdata.host屬性為<code>localhost</code>,mappingdata.contextpath.setstring(context.name)中context.name為<code>examples</code>,mappingdata.wrapperpath為<code>/jsp/jsp2/el/composite.jsp</code>,這驗證了mappingdata屬性的有效性,那麼mappingdata屬性是如何設定到request對象的屬性中的呢?

通過org.apache.catalina.connector.request的源碼可以知道,其是通過setcontextpath方法與sethost方法設定進去的,其源碼如下:

代碼清單5-5:

由于請求是從connector傳過來的,而coyoteadapter是connector中處理請求的最後一個類,那麼設定這兩個屬性的代碼肯定在coyoteadapter類中,果不其然:

代碼清單5-6:

intenalmap方法執行就是代碼清單5-4的内容,這樣就把從connector傳入請求,并設定request對象的mappingdata屬性的整個流程就打通了。還有一個疑問是為什麼mapper類中可以擁有container所有子容器的資訊呢?答案需要回到tomcat啟動過程圖的第21步的startintenal方法了:

代碼清單5-7:

這段代碼就是将mapperlistener作為一個監聽者加到整個container容器的每一個子容器中,這樣任何一個子容器發生變化,mapperlistener都将被通知,響應的mappingdata屬性也會改變。最後可以總結通路請求位址為http://localhost:8080/examples/composite.jsp的處理過程:

在端口8080啟動server,并通知service完成啟動,service通知connector完成初始化和啟動的過程

connector首先收到這個請求,會調用protocolhandler完成http協定的解析,然後交給socketprocessor處理,解析請求頭,再交給coyoteadapter解析請求行和請求體,并把解析資訊封裝到request和response對象中

把請求(此時應該是request對象,這裡的request對象已經封裝了http請求的資訊)交給container容器

container容器交給其子容器——engine容器,并等待engine容器的處理結果

engine容器比對其所有的虛拟主機,這裡比對到host

請求被移交給hostname為localhost的host容器,host比對其所有子容器context,這裡找到contextpath為/examples的context容器。如果比對不到就把該請求交給路徑名為”“的context去處理

請求再次被移交給context容器,context繼續比對其子容器wrapper,由wrapper容器加載composite.jsp對應的servlet,這裡編譯的servlet是basic_002dcomparisons_jsp.class檔案

context容器根據字尾比對原則*.jsp找到composite.jsp編譯的java類的class檔案

connector建構一個org.apache.catalina.connector.request以及org.apache.catalina.connector.response對象,使用反射調用servelt的service方法

context容器把封裝了響應消息的response對象傳回給host容器

host容器把response傳回給engine容器

engine容器傳回給connector

connetor容器把response傳回給浏覽器

浏覽器解析response封包

顯示資源内容

根據前面的内容,其中的映射關系是由mapperlistener類完成的。