从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)从而将责任传递到下一个阀门。
最后列出管道和过滤器链这两种责任链模式的对比:
- 从Tomcat中学习责任链模式
- [tomcat学习] 责任链模式实例
- tomcat责任链模式
- 责任链模式学习
- 责任链模式学习
- Tomcat中的设计模式--责任链模式
- Tomcat源码分析 filter 责任链模式
- JAVA中责任链模式
- 从真实项目中抠出来的设计模式——第三篇:责任链模式
- 从真实项目中抠出来的设计模式——第三篇:责任链模式
- 设计模式实例学习-责任链模式
- 设计模式学习--责任链模式
- 设计模式学习笔记--责任链模式
- 学习设计模式-责任链模式
- Tomcat设计模式-责任链模式(二)pipeline valve
- 设计模式之责任链模式(tomcat filters)
- 责任链模式(学习笔记)
- 学习日记-责任链设计模式
- 计算机视觉(ComputerVision, CV)相关领域的网站链接
- ubuntu 二进制安装mysql
- 使用Xcode8打包上传成功后,在iTunes Connect中不能构建版本
- div css sprites精灵-CSS图像拼合 CSS贴图定位网页背景素材图片拼合定位布局技术教程
- LeetCode---ugly number I and II
- 从Tomcat中学习责任链模式
- 相似度(参数调节代码)
- 自动缩放布局,以后再也不用担心适配问题了
- (转)聚类算法学习——ROCK 聚类算法
- web
- Python内置函数_集合操作类
- 史上最清晰的红黑树讲解(上)
- [转]最短路径算法—Dijkstra(迪杰斯特拉)算法分析与实现
- C++ map详解