之前我们已经把tomcat的源码分析完了,但是还有一个内容没有说,那就是Pipeline-Value管道,Tomcat中Container处理请求是使用Pipeline-Value管道模式来处理的。实际上这是责任链模式的一个变种,所以我们要优先说明下责任链模式
责任链模式
先讲一个故事:
古代女子通常是比较没地位的,如果一个妇女要出门,首先必须经过他父亲的同意,如果父亲没有回应就交给丈夫,如果丈夫也没回应则会交给儿子,也就是下面这张图的模式
这就是一个经典的责任链模式,我们用代码来实现
先定义一个处理类
public abstract class Handler {
public final static int FATHER_LEVEL_REQUEST = 1;
public final static int HUSBAND_LEVEL_REQUEST = 2;
public final static int SON_LEVEL_REQUEST = 3;
private int level = 0;
private Handler nextHandler;
public Handler(int _level){
this.level = _level;
}
public final void HandlerMessage(IWomen iWomen){
if (iWomen.getType() == level){
this.response(iWomen);
}else{
if (this.nextHandler != null ){
this.nextHandler.HandlerMessage(iWomen);
}else{
System.out.println("无人处理");
}
}
}
protected abstract void response(IWomen iWomen);
public void setNextHandler(Handler _handler){
this.nextHandler = _handler;
}
}
然后定义father/husband和son
public class Father extends Handler {
public Father() {
super(Handler.FATHER_LEVEL_REQUEST);
}
@Override
protected void response(IWomen iWomen) {
System.out.println("------------女儿向父亲指示----------");
System.out.println(iWomen.getRequest());
System.out.println("父亲的答案是:同意");
}
}
public class Husband extends Handler{
public Husband() {
super(Handler.HUSBAND_LEVEL_REQUEST);
}
@Override
protected void response(IWomen iWomen) {
System.out.println("------------妻子向丈夫指示----------");
System.out.println(iWomen.getRequest());
System.out.println("丈夫的答案是:同意");
}
}
public class Son extends Handler{
public Son() {
super(Handler.SON_LEVEL_REQUEST);
}
@Override
protected void response(IWomen iWomen) {
System.out.println("------------母亲向儿子指示----------");
System.out.println(iWomen.getRequest());
System.out.println("儿子的答案是:同意");
}
}
定义妇女接口
public interface IWomen {
int getType();
String getRequest();
}
实现类
public class Women implements IWomen{
private int type = 0;
private String request = "";
public Women(int _type,String _request){
this.type = _type;
switch (this.type){
case 1: this.request = "女儿的请求是:" + _request;
break;
case 2: this.request = "妻子的请求是:"+ _request;
break;
case 3 : this.request = "母亲的请求是" + _request;
}
}
@Override
public int getType() {
return this.type;
}
@Override
public String getRequest() {
return this.request;
}
}
场景
public class Client {
public static void main(String[] args) {
Random rand = new Random();
List<IWomen> lists = new ArrayList<>();
for (int i =0;i<5;i++){
lists.add(new Women(rand.nextInt(4),"我要去逛街"));
}
Handler father = new Father();
Handler husband = new Husband();
Handler son = new Son();
father.setNextHandler(husband);
husband.setNextHandler(son);
for (IWomen w :lists){
father.HandlerMessage(w);
}
}
}
以上就是责任链模式,责任链模式在框架中有大量应用,比如mybatis的Plugins,dubbo中的filter都运用了责任链模式
---------------------------------------------------------------------------------------------------------------------------------------------------
上面用一个简单的例子来说明了责任链模式,接下去我们来分析tomcat中的Pipeline-Value模式
tomcat中每个容器都有自己的Pipeline,比如Engine有EnginePipeline,每个pipeline都有特定的Value,而且都是在管道的最后一个执行,这个Value叫BaseValue,比如EnginePipeline的就叫StandardEngineValve。我们可以用下图来表示
toamcat中pipeline在ContainerBase中定义,在ContianerBase的startInternal方法中启动
pipeline中请求实现方法如下图
Connector–>Engine的Pipeline的ValveA中–>Engine Valve–>Host Pipeline的Error Report Valve和Host Value–>Context Valve–>Wrapper Valve中,在这里会经过一个过滤器链(Filter Chain)–>Servlet中。
Servlet处理完成后一步步返回,最后Connector拿到response。
一个pipeline包含多个Valve,这些阀共分为两类,一类叫基础阀(通过getBasic、setBasic方法调用),一类是普通阀(通过addValve、removeValve调用)。
一个管道一般有一个基础阀(通过setBasic添加),可以有0到多个普通阀(通过addValve添加)。
Pipeline嗲用所包含的Value的invoke方法来处理请求。最终会到达StandardWrapperValve
我们依次说明每个基础value
1 .StandardEngineValue
此阀门在调用时会获取请求主机的host对象,同时负责调用host对象管道中的第一个阀门。
public final void invoke(Request request,Response response) throws IOException, ServletException{
Host host = request.getHost();
host.getPipeline().getFirst().incoke(request,response);
}
2.StandardHostValue:
获取请求对应的上下文的context对象并调用context对象中管道的第一个阀门。
//触发request初始化事件
Context context = request.getContext();
//更新上次会话的访问时间
context.getPipeline().getFirst().invoke(request,response);
}
3.StandardContextValue
获取请求对应的wrapper对象,在向着客户端发送通知报文“HTTP/1/1 100 Continue”,最后调用wrapper对象中管道的第一个阀门。
public final void invoke(Request request,Response response) throws IOException,ServletException{
//判断访问路径是否包含WEB-INF或者META-INF,禁止访问此目录
Wrapper wrapper = request.getWrapper();
//向客户端发送"HTTP/1.1 100 Continue" 通知
wrapper.getPipeline().getFirst().invoke(request,response);
}
4.StandardWrapperValue,
请求在管道中最后到达StandardWrapperValve, 源码如下:
public final void invoke(Request request, Response response)
throws IOException, ServletException { ......
requestCount++;
//定位wrapper
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = ;
Context context = (Context) wrapper.getParent(); ......
// Allocate a servlet instance to process this request
try {
if (!unavailable) {
//加载servlet
servlet = wrapper.allocate();
}
} catch (UnavailableException e) {
......
}
......
// 根据配置建立一个filter-servlet的处理链表,servlet在链表的尾端
ApplicationFilterFactory factory =
ApplicationFilterFactory.getInstance();
ApplicationFilterChain filterChain =
factory.createFilterChain(request, wrapper, servlet);
// Reset comet flag value after creating the filter chain
request.setComet(false); // Call the filter chain for this request
// NOTE: This also calls the servlet's service() method
try {
String jspFile = wrapper.getJspFile();
if (jspFile != )
request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
else
request.removeAttribute(Globals.JSP_FILE_ATTR);
if ((servlet != ) && (filterChain != )) {
// Swallow output if needed
if (context.getSwallowOutput()) {
try {
SystemLogHandler.startCapture();
if (comet) {
filterChain.doFilterEvent(request.getEvent());
request.setComet(true);
} else {
//调用filter-servlet链表
filterChain.doFilter(request.getRequest(),
response.getResponse());
}
} finally {
String log = SystemLogHandler.stopCapture();
if (log != && log.length() > 0) {
context.getLogger().info(log);
}
}
} else {
if (comet) {
request.setComet(true);
filterChain.doFilterEvent(request.getEvent());
} else {
//调用filter-servlet链表
filterChain.doFilter
(request.getRequest(), response.getResponse());
}
} }
request.removeAttribute(Globals.JSP_FILE_ATTR);
} catch (ClientAbortException e) {
request.removeAttribute(Globals.JSP_FILE_ATTR);
throwable = e;
exception(request, response, e);
}
......
}
从StandardEngineValve开始, 所有的基础阀的实现最后都会调用其下一级容器,所有的普通阀都会执行getNext().invoke(request, response);,一直到StandardWrapperValve,完成请求处理过程。
因为Wrapper是对一个Servlet的包装,所以它的基础阀内部调用的过滤器链的doFilter方法和Servlet的service方法。
上述机制保证了请求传递到servlet去处理。