在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处理
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLyFGdz9lbvNWavw1cldWYtl2Lc12bj5SZ5VGdp5SesV2dvw1LcpDc0RHaiojIsJye.png)
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方法的代码片段
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLyFGdz9lbvNWavw1cldWYtl2Lc12bj5SZ5VGdp5SesV2dvw1LcpDc0RHaiojIsJye.png)
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()方法中,
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLyFGdz9lbvNWavw1cldWYtl2Lc12bj5SZ5VGdp5SesV2dvw1LcpDc0RHaiojIsJye.png)
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获取。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLyFGdz9lbvNWavw1cldWYtl2Lc12bj5SZ5VGdp5SesV2dvw1LcpDc0RHaiojIsJye.png)
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文件
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLyFGdz9lbvNWavw1cldWYtl2Lc12bj5SZ5VGdp5SesV2dvw1LcpDc0RHaiojIsJye.png)
// parse context level web.xml
inputsource contextwebxml = getcontextwebxmlsource();
parsewebxml(contextwebxml, webxml, false);
在getcontextwebxmlsource方法里
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLyFGdz9lbvNWavw1cldWYtl2Lc12bj5SZ5VGdp5SesV2dvw1LcpDc0RHaiojIsJye.png)
// servletcontext即core包下的applicationcontext
url = servletcontext.getresource(constants.applicationwebxml);
在getresource方法里
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLyFGdz9lbvNWavw1cldWYtl2Lc12bj5SZ5VGdp5SesV2dvw1LcpDc0RHaiojIsJye.png)
string hostname = context.getparent().getname();
因此,除非你自己实现一个contextconfig类,否则,你必须使用一个host容器。
管道(pipeline)包含该servlet容器将要调用的任务。一个阀(value)表示一个具体的执行任务。在servlet容器的管道中,有一个基础阀,但是,可以添加任意数量的阀。阀的数量指的是额外添加的阀数量,即不包括基础阀。有意思的是,可以通过server.xml来动态添加阀。
管道和阀的工作机制类似于servlet编程中的过滤器链和过滤器,tomcat的设计者采用的是链表数据结构来实现的链条机制,引入了一个类叫valuecontext。值得注意的是,基础阀总是最后执行。
请求最终会被引导到standardwrapper,本人也是首先从wrapper这一层来入手container的,直接看standardwrappervalue的invoke方法
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLyFGdz9lbvNWavw1cldWYtl2Lc12bj5SZ5VGdp5SesV2dvw1LcpDc0RHaiojIsJye.png)
@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实例的代码
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLyFGdz9lbvNWavw1cldWYtl2Lc12bj5SZ5VGdp5SesV2dvw1LcpDc0RHaiojIsJye.png)
public void deallocate(servlet servlet) throws servletexception {
// unlock and free this instance
synchronized (instancepool) {
countallocated.decrementandget();
instancepool.push(servlet);
instancepool.notify();
我们不考虑singlethreadmodel模型,因为较新版本的tomcat已经不用这种模型了(只有很老的版本才用),显然,通过上面的代码可以知道,常用的是线程池模型。下面给出加载servlet实例的代码
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLyFGdz9lbvNWavw1cldWYtl2Lc12bj5SZ5VGdp5SesV2dvw1LcpDc0RHaiojIsJye.png)
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的执行,
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLyFGdz9lbvNWavw1cldWYtl2Lc12bj5SZ5VGdp5SesV2dvw1LcpDc0RHaiojIsJye.png)
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]