从Tomcat中学习责任链模式

来源:互联网 发布:telnet本机ip端口不通 编辑:程序博客网 时间:2024/06/06 11:36

通过对Tomcat8.5.5版本的源码进行学习,发现Tomcat中有2个地方用到了责任链模式,一个是管道中的阀门,另一个是过滤器链中的过滤器。下面我们分别来看一下这两个责任链模式具体是如何使用的。

首先分析过滤器链中的过滤器:

注:以下示例代码摘自Tomcat8.5.5的源码并对其进行了简化,保留核心部分。

Filter接口如下所示:

public interface Filter {    public void doFilter(ServletRequest request, ServletResponse response,            FilterChain chain);}

Filter接口是所有具体过滤器必须实现的接口,该接口有一个核心的方法doFilter,方法对Request和Response进行处理,注意第三个参数类型为FilterChain,传入该参数的目的是为了责任能够传递到下一个过滤器。
具体Filter如下所示:

public class MyFilter implements Filter{    @Override    public void doFilter(ServletRequest request, ServletResponse response,            FilterChain chain) {            //处理request和response            //...            chain.doFilter(request,response);    }}

首先根据需求对request和response进行处理,最后需要调用过滤器链中的doFilter方法以便责任能够传递到下一个过滤器。chain.doFilter(request,response),注意这个doFilter是FilterChain中的doFilter与Filter中的doFilter没有任何关系,名字虽然相同,但是参数是不一样的。FilterChain中的doFilter只有Request和Response两个参数,其实更好的命名方式应该是chain.doNextFilter(resquest,response)

那么,FilterChain是什么样的呢?我们上面已经看到了FilterChain的用法,也知道了FilterChain在调用doFilter(request,response)的时候需要有能力将任务传递到下一个Filter,因此我们可以推断,FilterChain中需要保存所有的Filter。在Tomcat的实现中,正是在FilterChain中用一个数组保存了所有的Filter,这不禁让我想起了java.util.ArrayList的实现。
在Tomcat中FilterChain也是一个接口,如下所示:

public interface FilterChain {    public void doFilter(ServletRequest request, ServletResponse response);}

而ApplicationFilterChain实现了FilterChain,其核心代码如下所示:

final class ApplicationFilterChain implements FilterChain {    private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];    private int pos = 0;//维持过滤器链中的当前位置    private int n = 0;//过滤器链中的过滤器数量    public void doFilter(ServletRequest request, ServletResponse response) {        internalDoFilter(request,response);    }    private void internalDoFilter(ServletRequest request,                                  ServletResponse response) {        if(pos<n) {            ApplicationFilterConfig filterConfig = filters[pos++];            Filter filter = filterConfig.getFilter();            filter.doFilter(request, response, this);            return;        }    }    void addFilter(ApplicationFilterConfig filterConfig) {        //省略了扩容部分。。。        filters[n++] = filterConfig;    }}

可以看出在FilterChain的实现中确实是用数组来保存所有的过滤器,但是并不是直接保存的Filter类型,而是保存的FilterConfig类型,可以把FilterConfig当做Filter的包装类,我们可以通过filterConfig.getFilter()拿到Filter实例。在internalDoFilter方法中我们看到了FilterChain是如何实现责任的传递的,通过pos和n这两个变量来判断有没有传递到过滤器链的最后面,如果没有到最后,则取出当前过滤器并调用filter.doFilter的方法,在前面MyFilter的实现当中,它的doFilter方法在最后又调用了chain.doFilter(),从而保证了过滤器链能够将责任传递到下一个过滤器中。pos和n这两个变量的作用又让我联想到了ArrayList中的迭代器的实现。

客户端代码如下所示:

public class Client{    public static void main(String[] args) {        Request request = new Request();        Response response = new Response();        Filter filter1 = new MyFilter1();        Filter filter2 = new MyFilter2();        Filter filter3 = new MyFilter3();        FilterChain chain = new ApplicationFilterChain();        ApplicationFilterConfig filterConfig1 = new ApplicationFilterConfig(filter1);        ApplicationFilterConfig filterConfig2 = new ApplicationFilterConfig(filter2);        ApplicationFilterConfig filterConfig3 = new ApplicationFilterConfig(filter3);        chain.addFilter(filterConfig1);        chain.addFilter(filterConfig2);        chain.addFilter(filterConfig3);        chain.doFilter(request, response);    }}

下面再分析管道中的阀门是如何应用责任链模式的

上面我们提到了FilterChain中用数组保存了所有的过滤器,而在管道Pipeline中则采用的是链表的方式将一个个的阀门Valve链接起来的,这让我想起了java.util.LinkedList的实现,Pipeline相当于LinkedList,Valve相当于LinkedList中的内部类Node,不过Valve并不是以内部类的形式存在。
Valve接口如下所示:

public interface Valve {    public Valve getNext();    public void setNext(Valve valve);    public void invoke(Request request, Response response);}

具体的阀门类如下所示:

public class MyValve implements Valve {    protected Valve next = null;    @Override    public Valve getNext() {        return next;    }    @Override    public void setNext(Valve valve) {        this.next = valve;    }    @Override    public void invoke(Request request, Response response) {        //处理request,response        //...        getNext().invoke(request, response);        return;    }}

Pipeline接口如下所示:

public interface Pipeline{    public Valve getBasic();    public void setBasic(Valve valve);    public void addValve(Valve valve);    public Valve getFirst();}

Tomcat中Pipeline的实现类为StandardPipeline,核心代码如下所示:

public class StandardPipeline implements Pipeline {    protected Valve basic = null;    protected Valve first = null;    @Override    public Valve getBasic() {        return (this.basic);    }    @Override    public void setBasic(Valve valve) {        Valve oldBasic = this.basic;        if (oldBasic == valve)            return;        if (valve == null)            return;        Valve current = first;        while (current != null) {            if (current.getNext() == oldBasic) {                current.setNext(valve);                break;            }            current = current.getNext();        }        this.basic = valve;    }    @Override    public void addValve(Valve valve) {        // Add this Valve to the set associated with this Pipeline        if (first == null) {            first = valve;            valve.setNext(basic);        } else {            Valve current = first;            while (current != null) {                if (current.getNext() == basic) {                    current.setNext(valve);                    valve.setNext(basic);                    break;                }                current = current.getNext();            }        }    }    @Override    public Valve getFirst() {        if (first != null) {            return first;        }        return basic;    }}

可以看出StandardPipeline是一个具有头尾指针的单向链表。first相当于头指针,basic相当于尾指针。addValve方法的作用是把管道中的各个阀门链接起来。
在Tomcat中是通过容器组件对管道进行调用的,wrapper就是一种容器,因此可以通过如下方式进行调用:

wrapper.getPipeline().getFirst().invoke(request, response);

关于Tomcat的容器将会在其他的文章中进行分析。
从上面的调用方式可以看出,通过getPipeline()拿到管道对象,再调用管道对象的getFirst()拿到第一个阀门对象,再调用该阀门对象的invoke(request,response)方法,在该invoke方法的最后,如上面MyValve所示,会调用getNext().invoke(request, response)从而将责任传递到下一个阀门。

最后列出管道和过滤器链这两种责任链模式的对比:

管道/阀门 过滤器链/过滤器 管道(Pipeline) 过滤器链(FilterChain) 阀门(Valve) 过滤器(Filter) 底层实现为具有头(first)、尾(basic)指针的单向链表 底层实现为数组 Valve的核心方法invoke(request,response) Filter核心方法doFilter(request,response,chain) pipeline.getFirst().invoke(request,response) filterchain.doFilter(request,response)
0 0
原创粉丝点击