本文将从简单的场景引入, 逐步优化, 最后给出具体的责任链设计模式实现.

场景引入

  • 首先我们考虑这样一个场景: 论坛上用户要发帖子, 但是用户的想法是丰富多变的, 他们可能正常地发帖, 可能会在网页中浅入html代码, 可能会使用错误的表情格式, 也可能发送一些敏感信息.
  • 作为论坛的管理员必须对用户的帖子进行过滤才能显示出来, 否则论坛就经营不下去了. 现在我们考虑一种最简单处理方式.
public class Demo1 {
public static void main(String[] args) {
String msg = "大家好 :), <script>haha</script> 我要说超级敏感的话";//假设有一条这样的贴子
MsgProcessor mp = new MsgProcessor();
mp.setMsg(msg);//处理帖子
System.out.println(mp.process());
}
}
//帖子处理器
class MsgProcessor{
private String msg; public String process(){
//对html标签<>进行处理
String str = msg.replace("<", "[").replace(">", "]");
//对敏感字符尽心处理
str = str.replace("敏感", "正常");
//对错误的表情格式进行处理
str = str.replace(":)", "^_^");
return str;
}
//get() / set() 方法...
}
//输出结果
大家好 ^_^, [script]haha[/script] 我要说超级正常的话

 

责任链模型初体现

  • 通过上面的代码可以看到帖子处理器会对帖子进行不同的过滤, 我们可以把一种过滤方法对应为一个过滤器, 并且向上抽取出过滤器接口.
public class Demo2 {
public static void main(String[] args) {
String msg = "大家好 :), <script>haha</script> 我要说超级敏感的话";
MsgProcessor mp = new MsgProcessor();
mp.setMsg(msg);
System.out.println(mp.process());
}
} class MsgProcessor{
private String msg;
private Filter[] filters = {new HtmlFilter(), new SensitiveFilter(), new ExpressionFilter()};
public String process(){
for(Filter f : filters){
msg = f.doFilter(msg);
}
return msg;
} public String getMsg() {
return msg;
} public void setMsg(String msg) {
this.msg = msg;
}
}
//过滤器接口
interface Filter{
public String doFilter(String s);
}
//处理html标签
class HtmlFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace("<", "[").replace(">", "]");
}
}
//处理敏感词句
class SensitiveFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace("敏感", "正常");
}
}
//处理表情
class ExpressionFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace(":)", "^_^");
}
}

 

  • 上面的代码已经具备了责任链的模型. 在帖子发送到服务器的过程中, 它将依次经过3个过滤器, 这三个过滤器就构成一条过滤器链.

  • 下面我们考虑, 如果我们要在帖子处理过程中加入新的过滤器链条, 加在原链条的末尾或中间, 该怎么办呢?
  • 消息经过过滤器链条的过程会得到处理, 我们可以把过滤器链条看成一个过滤器, 让他也实现Filter接口, 那么就可以在一条过滤链中任意加入其他过滤器和过滤链了.
  • 下面的代码实现了过滤链FilterChain, 用过滤链替代原来的MsgProcessor.
public class Demo3 {
public static void main(String[] args) {
String msg = "大家好 :), <script>haha</script> 我要说超级敏感的话";//待处理的帖子
FilterChain fc1 = new FilterChain();//创建一条过滤器链1
fc1.add(new HtmlFilter())
.add(new SensitiveFilter());//往过滤器链1中添加过滤器 FilterChain fc2 = new FilterChain();//创建一条过滤器链2
fc2.add(new ExpressionFilter());//往过滤器链2中添加过滤器 fc1.add(fc2);//把过滤器链2当作过滤器添加到过滤器链1中,(过滤器链实现了Filter接口) msg = fc1.doFilter(msg);//使用过滤器链1对帖子进行过滤
System.out.println(msg);
}
} class FilterChain implements Filter{ private List<Filter> list = new ArrayList<>(); public FilterChain add(Filter filter){
this.list.add(filter);
return this;
} @Override
public String doFilter(String s) {
for(Filter f : list){
s = f.doFilter(s);
}
return s;
}
} class HtmlFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace("<", "[").replace(">", "]");
}
} class SensitiveFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace("敏感", "正常");
}
} class ExpressionFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace(":)", "^_^");
}
} interface Filter{
public String doFilter(String s);
}

 

更精巧设计, 展现责任链模式

  • 在继续优化之前, 我们考虑更现实的需求, 一个请求(发出一个帖子)作为数据报发送给服务器, 服务器除了需要对请求进行过滤外, 还需要给出响应, 并且可能要对响应也进行处理. 如下图所示

  • 当一个消息(包含请求体和响应体)发往服务器时, 它将依次经过过滤器1, 2, 3. 而当处理完成后, 封装好响应发出服务器时, 它也将依次经过过滤器3, 2, 1.
  • 大家可能会觉得有点像栈结构, 但是像归像, 这一逻辑应该如何实现呢?
  • 首先我们可以让过滤器持有过滤器链的引用, 通过调用过滤器链依次执行每个过滤器. 为了能让过滤器依次执行每个过滤器, 过滤器会持有一个index序号, 通过序号控制执行顺序. 至于后面对response的倒序请求, 则通过方法返回实现. 这部分设计纯用文字难以讲清, 请务必看下面的代码和代码后的分析, 配图.
  • 这个部分是责任链的精髓了, 懂了这部分代码, 看Web开发中的过滤器源码就没压力了.
public class Demo4 {
public static void main(String[] args) {
String msg = "大家好 :), <script>haha</script> 我要说超级敏感的话";//以下三行模拟一个请求
Request request = new Request();
request.setRequestStr(msg); Response response = new Response();//响应 FilterChain fc = new FilterChain();//过滤器链
HtmlFilter f1 = new HtmlFilter();//创建过滤器
SensitiveFilter f2 = new SensitiveFilter();
ExpressionFilter f3 = new ExpressionFilter();
fc.add(f1);//把过滤器添加到过滤器链中
fc.add(f2);
fc.add(f3); fc.doFilter(request, response, fc);//直接调用过滤器链的doFilter()方法进行处理
System.out.println(request.getRequestStr());
}
} interface Filter{
public void doFilter(Request request, Response response, FilterChain fc);
} class FilterChain implements Filter{ private List<Filter> list = new ArrayList<>(); private int index = 0; public FilterChain add(Filter filter){
this.list.add(filter);
return this;
} @Override
public void doFilter(Request request, Response response, FilterChain fc) {
if(index == list.size()){
return;//这里是逆序处理响应的关键, 当index为容器大小时, 证明对request的处理已经完成, 下面进入对response的处理.
}
Filter f = list.get(index);//过滤器链按index的顺序拿到filter
index++;
f.doFilter(request, response, fc);
} } class HtmlFilter implements Filter{
@Override
public void doFilter(Request request, Response response, FilterChain fc) {
request.setRequestStr(request.getRequestStr().replace("<", "[").replace(">","]"));
System.out.println("在HtmlFilter中处理request");//先处理request
fc.doFilter(request, response, fc);//调用过滤器链的doFilter方法, 让它去执行下一个Filter的doFilter方法, 处理response的代码将被挂起
System.out.println("在HtmlFilter中处理response");
}
} class SensitiveFilter implements Filter{
@Override
public void doFilter(Request request, Response response, FilterChain fc) {
request.setRequestStr(request.getRequestStr().replace("敏感", "正常"));
System.out.println("在SensitiveFilter中处理request");
fc.doFilter(request, response, fc);
System.out.println("在SensitiveFilter中处理response");
}
} class ExpressionFilter implements Filter{
@Override
public void doFilter(Request request, Response response, FilterChain fc) {
request.setRequestStr(request.getRequestStr().replace(":)", "^_^"));
System.out.println("在ExpressionFilter中处理request");
fc.doFilter(request, response, fc);
System.out.println("在ExpressionFilter中处理response");
}
} class Request{
private String requestStr;//真正的Request对象中是包含很多信息的, 这里仅用一个字符串作模拟 public String getRequestStr() {
return requestStr;
} public void setRequestStr(String requestStr) {
this.requestStr = requestStr;
}
} class Response{
private String responseStr; public String getResponseStr() {
return responseStr;
} public void setResponseStr(String responseStr) {
this.responseStr = responseStr;
}
}

 

  • 下面我描述一次整个过程, 你可以根据文字找到相应的代码进行理解.
  • 首先我们分别创建一个RequestResponse对象. Request在传入进后端时需要依次被过滤器1, 2, 3进行处理, Response对象在输出时要依次被过滤器3, 2, 1处理.
  • 创建好请求和响应对象后我们创建过滤器链, 并依次加入过滤器1, 2, 3. 整个处理流程将交给过滤器链决定.
  • 接着我们调用过滤器链的doFilter()方法对request对象进行处理
  • 这时过滤器链中的index值为0, 通过index我们找到第一个过滤器并调用它的doFilter()方法, 我们观察这段代码
class HtmlFilter implements Filter{
@Override
public void doFilter(Request request, Response response, FilterChain fc) {
request.setRequestStr(request.getRequestStr().replace("<", "[").replace(">","]"));
System.out.println("在HtmlFilter中处理request");//先处理request fc.doFilter(request, response, fc);//调用过滤器链的doFilter方法, 让它去执行下一个Filter的doFilter方法, 处理response的代码将被挂起 //在返回的过程中执行response
System.out.println("在HtmlFilter中处理response");
}
}
  • 进入doFilter()方法后, 首先会对request请求进行处理, 然后又调用了过滤器链的doFilter()方法. 这就是整个责任链模式的精妙之处, 它解释了为什么要给doFilter()加上一个过滤器链参数, 就是为了让每个过滤器可以调用过滤器链本身执行下一个过滤器.
  • 为什么要调用过滤器链本身? 因为当调用过滤器本身后, 程序将跳转回到过滤器链的doFilter方法执行, 这时index为1, 也就是拿到第二个过滤器, 然后继续处理.
  • 正是由于这个跳转, 使得过滤器中对response的处理暂时无法执行, 它必须等待上面的对过滤器链的方法返回才能被执行.
  • 所以最后我们将看到response响应被过滤器3, 2, 1(和请求倒序)执行.

  • 放大招了, 如果看了上面的图还是不懂, 欢迎给我留言.
  • 整个责任链模式已经从无到有展现出来了

 

阅读Tomcat中的Filter过滤器源码, 加深理解.

  • 相信通过上面的讲解, 你已经对整个责任链模式有了进一步的理解.
  • 下面我们通过阅读Tomcat里Filter的源码感受一下.
public interface Filter {
void init(FilterConfig var1) throws ServletException;
//熟悉的doFilter(), 熟悉的3个参数request, reponse, filterChain.
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException; void destroy();
}
  • 我们可以看到Filter接口的定义和我们的讲解的差不多, 只是多了初始化和销毁的方法.
public interface FilterChain {
void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}
  • 同时我们也看到它把FilterChain也向上抽取成接口, 不过这里的FilterChain没有实现Filter接口, 也就是说我们不能把两条FilterChain拼接在一起, 换个角度想Tomcat中的过滤器的可扩展性还没有我们例子中的好呢^_^

 

由浅入深讲解责任链模式,理解Tomcat的Filter过滤器的更多相关文章

  1. 设计模式——责任链(结合Tomcat中Filter机制)

    设计模式:责任链模式 说责任链之前,先引入一个场景,假如规定学生请假小于或等于 2 天,班主任可以批准:小于或等于 7 天,系主任可以批准:小于或等于 10 天,院长可以批准:其他情况不予批准:以此为 ...

  2. 设计模式学习笔记(十四)责任链模式实现以及在Filter中的应用

    责任链模式(Chain Of Responsibility Design Pattern),也叫做职责链,是将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求.当有请求发生时,可将请求沿着这条 ...

  3. 基于【 责任链模式】二 || 网关zuul过滤器封装

    一.基于责任链模式封装网关拦截 上一篇文章中已经使用建造者模式对网关拦截进行封装,存在一个问题,在连接器build中,每一个拦截都要进行true判断,代码看起来冗余,下面使用责任链模式封装 1.基于责 ...

  4. Java设计模式系列之责任链模式

    责任链模式 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道 ...

  5. JAVA设计模式之责任链模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述责任链(Chain of Responsibility)模式的: 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其 ...

  6. 设计模式《JAVA与模式》之责任链模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述责任链(Chain of Responsibility)模式的: 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其 ...

  7. 设计模式(13)--Chain of Responsibility(责任链模式)--行为型

    作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.模式定义: 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一 ...

  8. Java设计模式の责任链模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述责任链(Chain of Responsibility)模式的: 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其 ...

  9. java设计模式之责任链模式(Chain of Responsibility)

    转自:http://www.cnblogs.com/java-my-life/archive/2012/05/28/2516865.html 在阎宏博士的<JAVA与模式>一书中开头是这样 ...

随机推荐

  1. 3、js无缝滚动轮播

    另一个无缝滚动轮播,带暂停,由于js是异步,用C面向过程的思想开始会很怪异很怪异,因为当你定时器里面需要执行的函数时间比较长或是有一段延时时,异步的代码会完全不同,但习惯就好了. 这个代码有几个问题, ...

  2. 基于Microsoft Graph打造自己的Timeline应用

    原文链接:https://github.com/chenxizhang/office365dev/blob/e9b5a59cb827841d36692cc4ec52c11d43062e04/docs/ ...

  3. .NET开发设计模式-单例模式

    解释:单例模式主要特点:确保一个类只有一个实例,并提供一个访问它的全局访问点 意思就是说:在多线程的情况下:A先进行创建了该实例.B再进来访问时就不需要再创建了. using System; usin ...

  4. Webpack 热部署检测不到文件的变化

    最近在用webpack开发,突然发现热部署检测不到文件的变化,相关webpack的代码并没有发生改变,而且同事们的webpack都是正常的,不能热部署严重影响我的开发效率. 网上查了一下原来 Webp ...

  5. MySQL中查询时"Lost connection to MySQL server during query"报错的解决方案

    一.问题描述: mysql数据库查询时,遇到下面的报错信息: 二.原因分析: dw_user 表数据量比较大,直接查询速度慢,容易"卡死",导致数据库自动连接超时.... 三.解决 ...

  6. shell 常用命令语法简介

    一.grep用法 ************************************** ++++++用一些特殊的函数来处理参数++++++ *$# 传递给函数的参数个数 *$* 显示所有传递给 ...

  7. 沉默的螺旋--digest

    孤立的恐惧对名望的追求高度的和谐和共识是大部分人的幸福感.安全感的源头公开性能让某种行为具有社会可接受性 P5谈论和沉默决定了意见气候那些被新的东方政策说服的人,感觉自己所想的都是合理的.因此他们就会 ...

  8. JAVA PERSISTENCE API (JPA)

    13.2.1. About JPA The Java Persistence API (JPA) is the standard for using persistence in Java proje ...

  9. kafka 三个配置文件

    kafka的配置分为 broker.producter.consumer三个不同的配置 一 BROKER 的全局配置   最为核心的三个配置 broker.id.log.dir.zookeeper.c ...

  10. 排序系列 之 快速排序算法 —— Java实现

    基本思想: 通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变 ...