Tomcat源码分析(四)--容器处理链接之责任链模式(一)

2014-11-24 08:41:39 · 作者: · 浏览: 0

目标:在这篇文章希望搞明白connector.getContainer().invoke(request, response);调用容器的invoke后是怎么传递到 servlet或者jsp的?
StandardEngine没有invoke方法,它继承与ContainerBase(事实上所有的容器都继承于ContainerBase,在ContainerBase类有一些容器的公用方法和属性),抽象类ContainerBase的invoke方法如下:
[java]
protected Pipeline pipeline = new StandardPipeline(this);//标准管道的实现StandardPipeline
public void invoke(Request request, Response response)
throws IOException, ServletException {
pipeline.invoke(request, response);//调用管道里的invoke
}

由代码可知ContainerBase的invoke方法是传递到Pipeline,调用了Pipeline的invoke方法。这里要说一下Pipeline这个类,这是一个管道类,每一个管道类Pipeline包含数个阀类,阀类是实现了Valve接口的类,Valve接口声明了invoke方法。管道和阀的概念跟servlet编程里面的过滤器机制非常像,管道就像过滤器链,阀就好比是过滤器。不过管道中还有一个基础阀的概念,所谓基础阀就是在管道中当管道把所有的普通阀都调用完成后再调用的。不管是普通阀还是基础阀,都实现了Value接口,也都继承于抽象类ValveBase。在tomcat中,当调用了管道的invoke方法,管道则会顺序调用它里面的阀的invoke方法。先看看管道StandardPipeline的invoke方法:
[java]
public void invoke(Request request, Response response)
throws IOException, ServletException {
// Invoke the first Valve in this pipeline for this request
(new StandardPipelineva lveContext()).invokeNext(request, response);
}

其中StandardPipelineva lveContext是管道里的一个内部类,内部类的作用是帮助管道顺序调用阀Value的invoke方法,下面看它的定义代码:
[java]
protected class StandardPipelineva lveContext
implements ValveContext {
protected int stage = 0;
public String getInfo() {
return info;
}
public void invokeNext(Request request, Response response)
throws IOException, ServletException {
int subscript = stage;//阀的访问变量
stage = stage + 1;//当前访问到第几个阀
// Invoke the requested Valve for the current request thread
if (subscript < valves.length) {
valves[subscript].invoke(request, response, this);//管道的阀数组

} else if ((subscript == valves.length) && (basic != null)) {
basic.invoke(request, response, this);//当基础阀调用完成后,调用管道的基础阀的invoke阀
} else {
throw new ServletException
(sm.getString("standardPipeline.noValve"));
}
}
}
内部类StandardPipelineva lveContext的invokeNext方法通过使用局部变量来访问下一个管道数组,管道类的变量stage保存当前访问到第几个阀,valves保存管道的所有阀,在调用普通阀的invoke方法是,会把内部类StandardPipelineva lveContext本身传进去,这样在普通阀中就能调用invokeNext方法以便访问下一个阀的invoke方法,下面看一个普通阀的invoke方法:
[java]
public void invoke(Request request, Response response, ValveContext valveContext)
throws IOException, ServletException {
// Pass this request on to the next valve in our pipeline
valveContext.invokeNext(request, response);//使用调用下一个阀的invoke方法
System.out.println("Client IP Logger Valve");
ServletRequest sreq = request.getRequest();
System.out.println(sreq.getRemoteAddr());
System.out.println("------------------------------------");
}
这个阀的invoke方法,通过传进来到StandardPipelineva lveContext(实现了ValveContext接口)的invokeNext方法来实现调用下一个阀的invoke方法。然后简单的打印了请求的ip地址。
最后再看StandardPipelineva lveContext的invokeNext方法,调用完普通阀数组valves的阀后,开始调用基础阀basic的invoke方法,这里先说基础阀的初始化,在每一个容器的构造函数类就已经初始化了基础阀,看容器StandardEngine的构造函数:
[java]
public StandardEngine() {
super();
pipeline.setBasic(new StandardEngineva lve());//容器StandardEngine的基础阀StandardEngineva lve
}
即在容器构造的时候就已经把基础阀添加进管道pipeline中,这样在StandardPipelineva lveContext中的invokeNext方法里