责任链模式
责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。Tomcat中的Filter就是使用了责任链模式,创建一个Filter除了要在web.xml文件中做相应配置外,还需要实现javax.servlet.Filter接口。
为了方便理解,责任链模式直接用马士兵老师中的一个例子来讲解,做下笔记也方便自己以后的复习查询:
我们有一个字符串String msg = “:):,<script>,敏感,被就业,网络授课”;我们希望应用以下三个规则对字符串进行过滤和谐处理:
(1)将字符串中出现的”<>”符号替换成”[]”
(2)处理字符串中的敏感信息,将被就业和谐成就业
(3)将字符串中出现的”:):”转换成”^V^”;
字符串会依次运用这三条规则,对字符串进行处理,每个规则都有自己需要完成的责任和任务。
第一步:定义封装请求的类Request和封装处理结果响应的类Response
1 //封装请求的类Request 2 public class Request { 3 String requestStr; 4 5 public String getRequest() { 6 return requestStr; 7 } 8 9 public void setRequest(String request) { 10 this.requestStr = request; 11 } 12 13 }
1 //封装响应信息的类Response 2 public class Response { 3 String responseStr; 4 5 public String getResponse() { 6 return responseStr; 7 } 8 9 public void setResponse(String response) { 10 this.responseStr = response; 11 } 12 13 }
第二步:定义具有过滤功能的接口Filter,具体的过滤规则需要实现该接口
1 /* 2 * 定义接口Filter,具体的过滤规则需要实现这个接口,最后一个参数添加的意义是我们在Main函数中: 3 * fc.doFilter(request, response,fc);执行这一步的时候可以按照规则链条一次使用三个过滤规则对字符串进行处理 4 * 因为 5 * 6 */ 7 public interface Filter { 8 void doFilter(Request request,Response response,FilterChain chain); 9 }
第三步:定义具体的过滤处理规则
规则一
1 package com.bjsxt.dp.filter; 2 3 //处理字符串中的HTML标记 4 public class HTMLFilter implements Filter { 5 6 public void doFilter(Request request, Response response,FilterChain chain) { 7 //将字符串中出现的"<>"符号替换成"[]" 8 request.requestStr=request.requestStr 9 .replace('<', '[').replace('>', ']')+ 10 //后面添加的是便于我们观察代码执行步骤的字符串 11 "----HTMLFilter()"; 12 chain.doFilter(request, response,chain); 13 response.responseStr+="---HTMLFilter()"; 14 } 15 16 }
规则二
1 package com.bjsxt.dp.filter; 2 3 //定义的过滤敏感字眼的过滤规则 4 public class SensitiveFilter implements Filter{ 5 6 public void doFilter(Request request, Response response,FilterChain chain) { 7 //处理字符串中的敏感信息,将被就业和谐成就业 8 request.requestStr=request.requestStr 9 .replace("被就业", "就业").replace("敏感", "")+ 10 //后面添加的是便于我们观察代码执行步骤的字符串 11 " ---sensitiveFilter()"; 12 chain.doFilter(request, response,chain); 13 response.responseStr+="---sensitiveFilter()"; 14 } 15 16 }
规则三
1 package com.bjsxt.dp.filter; 2 3 //定义FaceFilter 4 public class FaceFilter implements Filter { 5 6 public void doFilter(Request request, Response response, FilterChain chain) { 7 8 //将字符串中出现的":):"转换成"^V^"; 9 request.requestStr = request.requestStr.replace(":):", "^V^") 10 //后面添加的是便于我们观察代码执行步骤的字符串 11 + "----FaceFilter()"; 12 chain.doFilter(request, response, chain); 13 response.responseStr += "---FaceFilter()"; 14 } 15 16 }
第四步:定义责任链FilterChain
1 package com.bjsxt.dp.filter; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 //过滤链条 7 public class FilterChain implements Filter{ 8 //用List集合来存储过滤规则 9 List<Filter> filters = new ArrayList<Filter>(); 10 //用于标记规则的引用顺序 11 int index=0; 12 //往规则链条中添加规则 13 public FilterChain addFilter(Filter f) { 14 filters.add(f); 15 //代码的设计技巧:Chain链添加过滤规则结束后返回添加后的Chain,方便我们下面doFilter函数的操作 16 return this; 17 } 18 public void doFilter(Request request,Response response,FilterChain chain){ 19 //index初始化为0,filters.size()为3,不会执行return操作 20 if(index==filters.size()){ 21 return; 22 } 23 //每添加一个过滤规则,index自增1 24 Filter f=filters.get(index); 25 index++; 26 //根据索引值获取对应的规律规则对字符串进行处理 27 f.doFilter(request, response, chain); 28 } 29 }
第五步:测试一下我们的代码
1 package com.bjsxt.dp.filter; 2 3 /* 4 * 责任链模式: 5 * 数据消息在进入数据库之前,要被多种过滤规则进行处理,多种规则形成一种链,依次处理 6 * 给定的数据消息 7 */ 8 public class Main { 9 public static void main(String args[]) { 10 //设定过滤规则,对msg字符串进行过滤处理 11 String msg = ":):,<script>,敏感,被就业,网络授课"; 12 //过滤请求 13 Request request=new Request(); 14 //set方法,将待处理字符串传递进去 15 request.setRequest(msg); 16 //处理过程结束,给出的响应 17 Response response=new Response(); 18 //设置响应信息 19 response.setResponse("response:"); 20 //FilterChain,过滤规则形成的拦截链条 21 FilterChain fc=new FilterChain(); 22 //规则链条添加过滤规则,采用的是链式调用 23 fc.addFilter(new HTMLFilter()) 24 .addFilter(new SensitiveFilter()) 25 .addFilter(new FaceFilter()); 26 //按照FilterChain的规则顺序,依次应用过滤规则 27 fc.doFilter(request, response,fc); 28 //打印请求信息 29 System.out.println(request.getRequest()); 30 //打印响应信息 31 System.out.println(response.getResponse()); 32 /* 33 * 处理器对数据进行处理 34 * --|----|---数据--|-----|--- 35 * 规则1 规则2 规则3 规则4 36 */ 37 } 38 }
运行结果:
^V^,[script],,就业,网络授课----HTMLFilter() ---sensitiveFilter()----FaceFilter()
response:---FaceFilter()---sensitiveFilter()---HTMLFilter()
代码可以使用Eclipse中设置断点,debug单步调试去验证,我们下面带着大家一块执行一下上面的代码。
(1)主函数中执行到fc.doFilter(request, response,fc);我们在此处设置断点(Eclipse设置断点的方式:在这行左面,双击那个竖边框的对应位置即可设置断点),现在的执行情况是下面这样,代码上面可以看到执行过程,现在停留在Main.main函数中。
(2)我们点击左上角的Step Into(F5)进入到doFilter(request,response,fc)中
现在执行位置在FilterChain.doFilter,依次执行代码,因为此时的index还是0,因此不可能执行return操作,跳过if代码块,调用索引值为0的规则HTMLFilter,index自增1,执行f.doFilter(request, response, chain);现在进入到了HTMLFilter类中的doFilter方法中
依次执行代码,对字符串请求进行处理,执行到chain.doFilter(request, response,chain)这一句,会再次进入FilterChain中的doFilter方法内
此时的index为1,仍然跳过if代码块执行下面的步骤,和上面一样,用索引值为1的规则进行处理,index自增1现在变成2了。执行f.doFilter(request, response, chain);会进入到SensitiveFilter中的doFilter方法执行
第三个规则的应用和前两个一致,直接给出执行到FaceFilter类中的doFilter方法的结果截图
现在执行到if判断,index的值为3,满足判断条件返回,上图中最上面的函数退栈,回到FaceFilter.doFilter函数中执行response.responseStr += “—FaceFilter()”;这就是我们运行结果中response中的第一部分。函数会依次退栈,response不断添加已经做过处理的规则的信息response.responseStr+=”—sensitiveFilter()”;response.responseStr+=”—HTMLFilter()”;最终回到Main.main函中打印reponse信息
运行过程用下图表示: