参考博文 : http://gearever.iteye.com/blog/1540028
pipleLine 是管道,而value可以理解成是阀门 , 容器与容器间通过管道连接
所以容器中有管道这个组件(定义在ContainerBase中),管道中又存在阀门,来控制信息的流动。
一个管道中可以有多个阀门,以链表的形式表示!!Tomcat 启动时候,这些组件就会相应的启动!
如果把Tomcat比作一台大的机器的话,那么Container ,pipeline ,value就是他内部的一些小零件,当机器启动的时候,
那么相应着的,必须使小零件转动。
StanardPipeline
Value (interface) ---- ValueBase --- (StanardEngineValue , StandardHostValue ..........) ;
Value 的各种子类,不同之处在于invoke()定义的业务逻辑的不同!也就是说,你这个阀门究竟要做什么事,由他来定义!
// coyoteAdapter service 方法: 这里是接着Tomcat接收请求那篇过来 ;
public void service(org.apache.coyote.Request req,
org.apache.coyote.Response res)
throws Exception {
// 因为Connector中有一个成员变量container 并不是他所在的容器,而是请求传递的地方,然后调用管道中第一个value(StandardEngineValue) ;
connector.getContainer().getPipeline().getFirst().invoke(request, response);
//省略若干代码
}
// StandardEngineValue
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Host to be used for this Request
Host host = request.getHost();
if (host == null) {
response.sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getServerName()));
return;
}
// Ask this Host to process this request
host.getPipeline().getFirst().invoke(request, response);
}
StandardHost中ErrorReportValue :
public void invoke(Request request, Response response)
throws IOException, ServletException {
// Perform the request 调用StandardHost的pipeLine 中下一个value
getNext().invoke(request, response);
//省略若干代码
}
context.getPipeline().getFirst().invoke(request, response);
wrapper.getPipeline().getFirst().invoke(request, response);
可以定制化Value , 通常在server.xml中配置,只要继承了org.apache.catalina.valves.ValveBase
<Engine name="Catalina" defaultHost="localhost">
<Valve className="MyValve0"/>
<Valve className="MyValve1"/>
<Valve className="MyValve2"/>
……
<Host name="localhost" appBase="webapps">
</Host>
</Engine>
当定制了Value时,就会调用pipeLine 中add方法,把Value添加到相应的管道中!
就像我们在上面看到的一样,每个容器都要一个缺省的(就是不要在server.xml)中配置的Value;
他们总是位于value链的末端!最后才被调用!就像上面看到的一样,我们调用的都是第一个,并没有看到它调用了整个value链啊。
原因就在这,因为这些每个容器默认的value 永远都是在最后一个,所以不存在会调用下一个的情况(getNext()),但是,像自己定义的
的Value , 那么你在实现invoke()的时候,内部就必须getNext(),即调用下一个Value的操作,就像上面的ErrorReportValue一样!
看下面的addvalue() :
public void addValve(Valve valve) {
// Validate that we can add this Valve
if (valve instanceof Contained)
((Contained) valve).setContainer(this.container);
// Start the new component if necessary
if (started) {
if (valve instanceof Lifecycle) {
try {
((Lifecycle) valve).start();
} catch (LifecycleException e) {
log.error("StandardPipeline.addValve: start: ", e);
}
}
// Register the newly added valve
registerValve(valve);
}
// Add this Valve to the set associated with this Pipeline
if (first == null) {
first = valve;
valve.setNext(basic); // basic 就是缺省的 添加到最后
} else {
Valve current = first;
while (current != null) { //这里在一个链表中找到basic , 然后在它前面插入
if (current.getNext() == basic) {
current.setNext(valve);
valve.setNext(basic);
break;
}
current = current.getNext();
}
}
}
四大容器的标准valve的调用逻辑图: