(轉(zhuǎn)載)Tomcat中Servlet的創(chuàng)建初始化體系結(jié)構(gòu)及工作
聲明:本文是轉(zhuǎn)載,但原網(wǎng)址已經(jīng)不存在,因此抱歉無法給出地址。有些圖也沒法查看,本人已進(jìn)行部分刪減。
一個?Web?應(yīng)用對應(yīng)一個?Context?容器,在Tomcat中Context容器就是管理?Servlet的,是Servlet?運行時的?Servlet容器,添加一個?Web?應(yīng)用時將會創(chuàng)建一個?StandardContext?容器,并且給這個?Context?容器設(shè)置必要的參數(shù),url?和path?分別代表這個應(yīng)用在?Tomcat?中的訪問路徑和這個應(yīng)用實際的物理路徑。
Web?應(yīng)用的初始化工作
? ? ? ? ?對于一個Tomcat中的Web?應(yīng)用,它的初始化工作是在ContextConfig的configureStart方法中實現(xiàn)的,應(yīng)用的初始化主要是要解析?web.xml?文件,這個文件描述了一個?Web?應(yīng)用的關(guān)鍵信息,也是一個Web?應(yīng)用的入口。Tomcat?首先會找?globalWebXml?這個文件是在以下兩個文件中的任一個org/apache/catalin/startup/NO_DEFAULT_XML?或?conf/web.xml。接著會找?hostWebXml,即尋找應(yīng)用的配置文件examples/WEB-INF/web.xml。web.xml?文件的各個配置項將會被解析成相應(yīng)的屬性保存在WebXml對象中。接下去將會將?WebXml?對象中的屬性設(shè)置到?Context?容器中,這里包括創(chuàng)建?Servlet?對象、filter、listener?等等。
然后將Servlet?包裝成?StandardWrapper?并作為子容器添加到?Context?中,則Context?容器才是真正運行?Servlet?的?Servlet?容器。一個?Web?應(yīng)用對應(yīng)一個?Context?容器,容器的配置屬性由應(yīng)用的web.xml?指定。
創(chuàng)建?Servlet?對象
如果?Servlet?的?load-on-startup?配置項大于0,那么在?Context?容器啟動的時候就會被實例化,前面提到在解析配置文件時會讀取默認(rèn)的globalWebXml,在?conf?下的?web.xml?文件中定義了一些默認(rèn)的配置項,其定義了兩個Servlet,分別是:org.apache.catalina.servlets.DefaultServlet?和org.apache.jasper.servlet.JspServlet?它們的?load-on-startup?分別是?1?和?3,也就是當(dāng)?Tomcat?啟動時這兩個?Servlet?就會被啟動,JspServlet 這個主要就是為了將JSP頁面翻譯成一個Servlet的,它會攔截所有的以.jsp后綴名的文件。
創(chuàng)建?Servlet?實例的方法是從?Wrapper. loadServlet開始的。loadServlet?方法要完成的就是獲取servletClass?然后把它交給?InstanceManager?去創(chuàng)建一個基于?servletClass.class?的對象。
初始化?Servlet
由于Servlet 在Web初始化的時候已經(jīng)包裝在StandardWrapper?,則對于初始化?Servlet,是在StandardWrapper的initServlet方法中,這個方法很簡單就是調(diào)用?Servlet?的?init?的方法,同時把包裝了?StandardWrapper?對象的?StandardWrapperFacade?作為?ServletConfig?傳給?Servlet。
如果該?Servlet?關(guān)聯(lián)的是一個?jsp?文件,那么前面初始化的就是?JspServlet,接下去會模擬一次簡單請求,請求調(diào)用這個?jsp?文件,以便編譯這個?jsp?文件為?class,并初始化這個?class。
Servlet?體系結(jié)構(gòu)
我們知道?Java Web?應(yīng)用是基于?Servlet?規(guī)范運轉(zhuǎn),下面介紹一下Servlet的體系結(jié)構(gòu)。
Servlet?頂層類關(guān)聯(lián)圖

從上圖可以看出?Servlet?規(guī)范就是基于這幾個類運轉(zhuǎn)的,與?Servlet?主動關(guān)聯(lián)的是三個類,分別是ServletConfig、ServletRequest?和?ServletResponse。這三個類都是通過容器傳遞給?Servlet?的,其中ServletConfig?是在?Servlet?初始化時就傳給?Servlet?了,而后兩個是在請求達(dá)到時調(diào)用?Servlet?時傳遞過來的。我們很清楚?ServletRequest?和?ServletResponse?在?Servlet?運行的意義,ServletConfig對?Servlet主要是為了獲取這個?Servlet?的一些配置屬性,而這些配置屬性可能在?Servlet?運行時被用到。Servlet的運行模式是一個典型的“握手型的交互式”運行模式。所謂“握手型的交互式”就是兩個模塊為了交換數(shù)據(jù)通常都會準(zhǔn)備一個交易場景,這個場景一直跟隨這個交易過程直到這個交易完成為止。這個交易場景的初始化是根據(jù)這次交易對象指定的參數(shù)來定制的,這些指定參數(shù)通常就會是一個配置類。所以對號入座,交易場景就由?ServletContext?來描述,而定制的參數(shù)集合就由?ServletConfig?來描述。而ServletRequest?和?ServletResponse?就是要交互的具體對象了,它們通常都是作為運輸工具來傳遞交互結(jié)果。
?StandardWrapper?和?StandardWrapperFacade?都實現(xiàn)了?ServletConfig?接口,而StandardWrapperFacade?是?StandardWrapper?門面類。所以傳給?Servlet?的是StandardWrapperFacade?對象,這個類能夠保證從?StandardWrapper?中拿到?ServletConfig?所規(guī)定的數(shù)據(jù),而又不把?ServletConfig?不關(guān)心的數(shù)據(jù)暴露給?Servlet。
同樣?ServletContext?也與?ServletConfig?有類似的結(jié)構(gòu),Servlet?中能拿到的?ServletContext?的實際對象也是?ApplicationContextFacade?對象。ApplicationContextFacade?同樣保證?ServletContext?只能從容器中拿到它該拿的數(shù)據(jù),它們都起到對數(shù)據(jù)的封裝作用,它們使用的都是門面設(shè)計模式也就是外觀模式。
通過?ServletContext?可以拿到?Context?容器中一些必要信息,比如應(yīng)用的工作路徑,容器支持的Servlet?最小版本等。
我們在創(chuàng)建自己的?Servlet?類時通常使用的都是?HttpServletRequest?和?HttpServletResponse,它們繼承了ServletRequest?和?ServletResponse。為何?Context?容器傳過來的?ServletRequest、ServletResponse?可以被轉(zhuǎn)化為?HttpServletRequest?和?HttpServletResponse?呢?
Tomcat?一接受到請求首先將會創(chuàng)建?org.apache.coyote.Request和?org.apache.coyote.Response,這兩個類是?Tomcat?內(nèi)部使用的描述一次請求和相應(yīng)的信息類它們是一個輕量級的類,它們作用就是在服務(wù)器接收到請求后,經(jīng)過簡單解析將這個請求快速的分配給后續(xù)線程去處理,所以它們的對象很小,很容易被?JVM?回收。接下去當(dāng)交給一個用戶線程去處理這個請求時又創(chuàng)建org.apache.catalina.connector.?Request和?org.apache.catalina.connector.?Response對象。這兩個對象一直穿越整個?Servlet?容器直到要傳給?Servlet,傳給?Servlet?的是?Request?和?Response?的門面類RequestFacade?和?RequestFacade,這里使用門面模式與前面一樣都是基于同樣的目的——封裝容器中的數(shù)據(jù)。
Servlet?如何工作

當(dāng)用戶從瀏覽器向服務(wù)器發(fā)起一個請求,通常會包含如下信息:http://hostname: port /contextpath/servletpath,
hostname?和?port?是用來與服務(wù)器建立?TCP?連接,而后面的?URL?才是用來選擇服務(wù)器中那個子容器服務(wù)用戶的請求。
在Tomcat7.0,這種映射工作用一個專門的類來完成的,這個就是org.apache.tomcat.util.http.mapper,這個類保存了?Tomcat?的?Container?容器中的所有子容器的信息,當(dāng)org.apache.catalina.connector. Request?類在進(jìn)入?Container?容器之前,mapper?將會根據(jù)這次請求的hostname?和?contextpath?將?host?和?context?容器設(shè)置到?Request?的?mappingData?屬性中。所以當(dāng)Request?進(jìn)入?Container?容器之前,它要訪問那個子容器這時就已經(jīng)確定了。
當(dāng)Rquest?請求到達(dá)了最終的?Wrapper?容器后,此時還需要真正的到達(dá)最終的?Servlet?,這里還需要一些步驟,必須要執(zhí)行?Filter?鏈,以及要通知你在?web.xml?中定義的?listener。
接下去就要執(zhí)行?Servlet?的?service?方法了,通常情況下,我們自己定義的?servlet?并不是直接去實現(xiàn)?javax.servlet.servlet?接口,而是去繼承更簡單的?HttpServlet?類或者?GenericServlet?類,我們可以有選擇的覆蓋相應(yīng)方法去實現(xiàn)我們要完成的工作。
Servlet?的確已經(jīng)能夠幫我們完成所有的工作了,但是現(xiàn)在的?web?應(yīng)用很少有直接將交互全部頁面都用?servlet?來實現(xiàn),而是采用更加高效的?MVC?框架來實現(xiàn)。這些?MVC?框架基本的原理都是將所有的請求都映射到一個?Servlet,然后去實現(xiàn)?service?方法,這個方法也就是?MVC?框架的入口。
? ?當(dāng)?Servlet?從?Servlet?容器中移除時,也就表明該?Servlet?的生命周期結(jié)束了,這時?Servlet?的destroy?方法將被調(diào)用,做一些掃尾工作。