参考博文 : 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,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的调用逻辑图: