在coyoteadapter的service方法中,主要幹了2件事:
1. org.apache.coyote.request -> org.apache.catalina.connector.request extends httpservletrequest
org.apache.coyote.response -> org.apache.catalina.connector. response extends httpservletresponse
context和wrapper定位
2. 将請求交給standardenginevalue處理
public void service(org.apache.coyote.request req,
org.apache.coyote.response res) {
//
postparsesuccess = postparserequest(req, request, res, response);
connector.getservice().getcontainer().getpipeline().getfirst().invoke(request, response);
}
postparserequest方法的代碼片段
connector.getmapper().map(servername, decodeduri, version,
request.getmappingdata());
request.setcontext((context) request.getmappingdata().context);
request.setwrapper((wrapper) request.getmappingdata().wrapper);
request通過uri的資訊找到屬于自己的context和wrapper。而這個mapper儲存了所有的容器資訊,不記得的同學可以回到connector的startinternal方法中,最有一行代碼是mapperlistener.start()。在mapperlistener的start()方法中,
public void startinternal() throws lifecycleexception {
setstate(lifecyclestate.starting);
finddefaulthost();
engine engine = (engine) connector.getservice().getcontainer();
addlisteners(engine);
container[] conhosts = engine.findchildren();
for (container conhost : conhosts) {
host host = (host) conhost;
if (!lifecyclestate.new.equals(host.getstate())) {
registerhost(host);
}
}
在容器初始化和變化時都會觸發監聽事件,進而将所有容器資訊儲存在mapper中。之是以叫mapper,因為它的主要作用就是定位wrapper,而我們在web.xml裡也配了filter/servlet-mapping。
另外,由上面的代碼可知,在随後的請求路線中,engine可有connector擷取,context和wrapper可直接由request擷取,host也可由request擷取。
public host gethost() { return ((host) mappingdata.host); }
上面的代碼中還涉及到了兩個很重要的概念--pipeline和value,我們不妨先一睹container的調用鍊和時序圖。
對于每個引入的http請求,連接配接器都會調用與其關聯的servlet容器的invoke方法。然後,servlet容器會調用其所有子容器的invoke方法。為什麼必須要有一個host容器呢?
在tomcat的實際部署中,若一個context執行個體使用contextconfig對象進行設定,就必須使用一個host對象,原因如下:
使用contextconfig對象需要知道應用程式web.xml檔案的位置,在其webconfig()方法中會解析web.xml檔案
// parse context level web.xml
inputsource contextwebxml = getcontextwebxmlsource();
parsewebxml(contextwebxml, webxml, false);
在getcontextwebxmlsource方法裡
// servletcontext即core包下的applicationcontext
url = servletcontext.getresource(constants.applicationwebxml);
在getresource方法裡
string hostname = context.getparent().getname();
是以,除非你自己實作一個contextconfig類,否則,你必須使用一個host容器。
管道(pipeline)包含該servlet容器将要調用的任務。一個閥(value)表示一個具體的執行任務。在servlet容器的管道中,有一個基礎閥,但是,可以添加任意數量的閥。閥的數量指的是額外添加的閥數量,即不包括基礎閥。有意思的是,可以通過server.xml來動态添加閥。
管道和閥的工作機制類似于servlet程式設計中的過濾器鍊和過濾器,tomcat的設計者采用的是連結清單資料結構來實作的鍊條機制,引入了一個類叫valuecontext。值得注意的是,基礎閥總是最後執行。
請求最終會被引導到standardwrapper,本人也是首先從wrapper這一層來入手container的,直接看standardwrappervalue的invoke方法
@override
public final void invoke(request request, response response) {
requestcount++;
standardwrapper wrapper = (standardwrapper) getcontainer();
servlet servlet = null;
context context = (context) wrapper.getparent();
// allocate a servlet instance to process this request
try {
if (!unavailable) {
servlet = wrapper.allocate();
}
} catch (exception e) {}
// create the filter chain for this request
applicationfilterfactory factory =
applicationfilterfactory.getinstance();
applicationfilterchain filterchain =
factory.createfilterchain(request, wrapper, servlet);
// call the filter chain for this request
// note: this also calls the servlet's service() method
filterchain.dofilter(request.getrequest(), response.getresponse());
// release the filter chain (if any) for this request
if (filterchain != null) filterchain.release();
// deallocate the allocated servlet instance
if (servlet != null) wrapper.deallocate(servlet);
上面代碼中最重要的三處邏輯就是servlet執行個體的擷取與解除安裝和filter鍊調用。我們先看解除安裝servlet執行個體的代碼
public void deallocate(servlet servlet) throws servletexception {
// unlock and free this instance
synchronized (instancepool) {
countallocated.decrementandget();
instancepool.push(servlet);
instancepool.notify();
我們不考慮singlethreadmodel模型,因為較新版本的tomcat已經不用這種模型了(隻有很老的版本才用),顯然,通過上面的代碼可以知道,常用的是線程池模型。下面給出加載servlet執行個體的代碼
public servlet allocate() throws servletexception {
instance = loadservlet();
initservlet(instance);
synchronized (instancepool) {
while (countallocated.get() >= ninstances) {
// allocate a new instance if possible, or else wait
if (ninstances < maxinstances) {
try {
instancepool.push(loadservlet());
ninstances++;
} catch (throwable e) {
}
} else {
instancepool.wait();
} catch (interruptedexception e) {
// ignore
}
}
countallocated.incrementandget();
return instancepool.pop();
最後,我們來看看filterchain的執行,
public void dofilter(servletrequest request, servletresponse response)
throws ioexception, servletexception {
internaldofilter(request,response);
private void internaldofilter(servletrequest request,
servletresponse response)
// call the next filter if there is one
if (pos < n) {
filter.dofilter(request, response, this);
// we fell off the end of the chain -- call the servlet instance
servlet.service(request, response);
顯然,在調用web.xml裡配的某個servlet時,都會先依次調用在web.xml裡配的filter,這可謂是責任鍊設計模式的一種經典實作。
好了,現在你可以把前文中connector執行過程和本文的container執行過程結合起來了。我始終相信,深入一點,你會更快樂。
原文連結:[http://wely.iteye.com/blog/2295240]