摘要:本笔记主要记录了责任链设计模式的设计、从问题的提出、到问绕解决问题的途径来展示责任链设计模式的思想与代码构建。
在实际项目中、我们常常需要对请求中所带来的信息进行处理。比如说我们经常会使用Filter来进行编码的处理、如果学过struts2、那么你对Interceptor一定不会陌生、struts2就是通过一系列的Interceptor将请求中的参数与request剥离开来供我们使用的。
下面会围绕论坛发帖的形式来使用Java实现责任链的设计模式。问题:当你在论坛发帖的时候、会不会有些时候某些内容是禁止发帖的?发了还会被封号?当然别想太多。问题简单化:如何对传进来的一个字符串进行一定的处理?
使用极限编程的形式:先写测试程序、再写相应的类。
1、测试类Main:
package com.chy.filter.test; import com.chy.filter.MsgProcessor; public class Main { public static void main(String[] args) { String msg = "大家好 :),<script>,被强迫, 敏感词汇,等等"; MsgProcessor mp = new MsgProcessor(); mp.setMsg(msg); String result = mp.process(); System.out.println(result); } }
2、处理类MsgProcessor:
package com.chy.filter; public class MsgProcessor { private String msg; public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public String process(){ msg = msg.replace("<", "[").replace(">", "]"); msg = msg.replace("被强迫", "无奈"); return msg; } }
处理类很简单、就是对Main类中的字符串进行两个处理:a)替换html标签。b)替换敏感词汇。
上述内容应该是很小菜一碟的事情、那么如果某天再想处理一下传入的字符串呢?处理的方式又想变一下呢?当然可以在MsgProcessor中加、但是有没有一种让我们加的比较方便、或者比较舒服的方式呢?简单的说就是:可以动态的指定过滤的方式和规则。具体的实现:模拟Servlet中Filter的实现。
1、定义一个专门用于处理字符串的接口——Filter、他提供一个方法doFilter(Stringstr)用于处理字符串。
package com.chy.filter; public interface Filter { public String doFilter(String str); }
2、如果我们想对某一字符串进行特定的处理的时候、比如处理字符串中HTML标签、则我们可以定义一个HTMLFilter实现Filter、重写doFilter方法:
package com.chy.filter; public class HTMLFilter implements Filter { @Override public String doFilter(String str) { //process the html tag<> String r = str.replace("<", "[").replace(">", "]"); return r; } }
3、同样的对于过滤敏感词汇的处理、我们可以建立一个SensitiveFilter、重写doFilter方法:
package com.chy.filter; public class SesitiveFilter implements Filter{ @Override public String doFilter(String str) { //process the sensitive words str = str.replace("被强迫", "无奈"); return str; } }
4、在MsgProcessor中调用这两个Filter处理msg、为了以后能使用指定的Filter来过滤字符串、我们可以在MsgProcessor中定义一个Filter数组、将我们想要使用的Filter都存放在这个数组中、然后在处理字符串的地方依次调用Filter来过滤、这样就达到了使用指定规则、指定方式过滤的目的、在项目中我们更加灵活的可以使用配置文件来指定这些Filter!MsgProcessor代码:
package com.chy.filter; public class MsgProcessor { private String msg; private Filter[] filters = {new HTMLFilter(), new SesitiveFilter()}; public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public String process(){ for(Filter filter : filters){ msg = filter.doFilter(msg); } return msg; } }
上面的一个个对字符串的过滤器连接起来是不是很像一个链条?链条的环就是由一个一个过滤器组成的、并且我们可以随意组装这些环、不喜欢那个还可以直接抽掉。这样就在原来的基础上只修改Filter数组的情况下实现了对字符串的过滤。如果使用配置文件、那就更爽了、想怎么指定就怎么指定、而不用修改MsgProcessor的代码;
到前面会不会觉得责任链就已经很爽了?下面考虑这样一个问题:如果说我原来有一个Filter数组用来处理字符串了、那么我现在又有一个新的Filter链同样是处理字符串的、我想直接把新的Filter链加入到原来Filter数组的指定位置、怎么弄?
当然我们可能会想到直接copy代码、copy代码可以解决、如果有一万那、每次是不是要copy一万下?也有可能想到使用一个新的ArrayList、将两个Filter链全都加进来、是一种方法、还有没有更好的呢?
1、使用FilterChain!并且这个FilterChain同样实现了Filter接口——FilterChain:
package com.chy.filter; import java.util.ArrayList; import java.util.List; public class FilterChain implements Filter{ //用于存放所有Filter List<Filter> filters = new ArrayList<Filter>(); //添加Filter public FilterChain addFilter(Filter f){ filters.add(f); return this; } //移除一个Filter public void removeFilter(Filter f){ filters.remove(f); } //使用这个FilterChain来过滤字符串 public String doFilter(String str){ for(Filter filter : filters){ str = filter.doFilter(str); } return str; } }
2、这样的话、在MsgProcessor中就不必再手动的new Filter数组了。直接定义一个FilterChain、并提供get、set方法。这样我们在使用MsgProcessor的时候就可以设置FilterChain了、并调用process方法处理字符串。
package com.chy.filter; public class MsgProcessor { private String msg; private FilterChain filterChain; public FilterChain getFilterChain() { return filterChain; } public void setFilterChain(FilterChain filterChain) { this.filterChain = filterChain; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public String process(){ return filterChain.doFilter(msg); } }
3、这样就很简单的实现了我们上面那种将一个FilterChain添加到另一个FilterChain中来解决在一个FilterChain中添加另一个FilterChain的功能了。Main代码:
package com.chy.filter; public class Main { public static void main(String[] args) { String msg = "大家好 :),<script>,被强迫, 敏感词汇,等等"; MsgProcessor mp = new MsgProcessor(); mp.setMsg(msg); //构造第一个FilterChain FilterChain fc = new FilterChain(); fc.addFilter(new HTMLFilter()).addFilter(new SesitiveFilter()); //构造第二个FilterChain、并且将新的FilterChain放入原来的FilterChain中。 FilterChain fc2 = new FilterChain().addFilter(new FaceFilter()); fc.addFilter(fc2); mp.setFilterChain(fc); String result = mp.process(); System.out.println(result); } }
result:
大家好 ^_^,[script],无奈, 敏感词汇,等等
问题到上面视乎得到了很好的解决、但是实际开发中、我们一般从客户端发送消息到服务器端、此时我们使用一个FilterChain来处理request、当服务器端处理完成之后一般会给客户端一个反馈、response、那么我们怎么来过滤这个response呢?最直接的就是再写一个FilterChain啊、当然可以、只是这样的结构是不是有一点点的不完美呢?能不能这个FilterChain既能处理客户端发往服务器端的消息、也可以处理服务器端返回的消息呢?
下面通过模拟web中对request、response的处理来看看如何实现的
1、新建一个项目java_web_filter
2、定义两个类:Request、Response、这两个类在实际项目中包含有非常多的信息、简单起见、我们只在其中分别定义一个字符串、来表示其包含的信息——Reques、Response代码:
package com.chy.filter.web; public class Request { private String requestStr; public String getRequestStr() { return requestStr; } public void setRequestStr(String requestStr) { this.requestStr = requestStr; } } package com.chy.filter.web; public class Response { private String responseStr; public String getResponseStr() { return responseStr; } public void setResponseStr(String responseStr) { this.responseStr = responseStr; } }
3、定义一个Filter接口、用于处理Request、Response对象中的我们自己定义的字符串——Filter代码:
package com.chy.filter.web; public interface Filter { public void doFilter(Request request, Response response); }
4、同样使用FilterChain的方式来进行链式处理——FilterChain代码:
package com.chy.filter.web; import java.util.ArrayList; import java.util.List; public class FilterChain implements Filter{ //用于存放所有Filter List<Filter> filters = new ArrayList<Filter>(); int index = 0; //添加Filter public FilterChain addFilter(Filter f){ filters.add(f); return this; } //移除一个Filter public void removeFilter(Filter f){ filters.remove(f); } //使用这个FilterChain来过滤字符串 public void doFilter(Request request, Response response) { for(Filter filter : filters){ filter.doFilter(request, response); } } }
5、定义一个处理字符串的HTML标签的Filter——HTMLFilter代码:
package com.chy.filter.web; public class HTMLFilter implements Filter { @Override public void doFilter(Request request, Response response) { request.setRequestStr(request.getRequestStr().replace("<", "[").replace(">", "]")+"--------HTMLFilter"); } }
6、定义一个处理敏感词汇的Filter——SensitiveFilter代码:
package com.chy.filter.web; public class SesitiveFilter implements Filter{ @Override public void doFilter(Request request, Response response) { request.setRequestStr(request.getRequestStr().replace("被强迫", "无奈") + "------SensitiveFilter"); } }
7、直接在Main方法中调用FilterChain对Request、Response进行处理——Main代码:
package com.chy.filter.web; public class Main { public static void main(String[] args) { String msg = "大家好 :),<script>,被强迫, 敏感词汇,等等"; Request request = new Request(); request.setRequestStr(msg); Response response = new Response(); response.setResponseStr(msg); FilterChain fc = new FilterChain(); fc.addFilter(new HTMLFilter()).addFilter(new SesitiveFilter()); fc.doFilter(request, response); System.out.println(request.getRequestStr()); System.out.println(response.getResponseStr()); } }
从这里我们依然没有看到对Response的处理。下面开始处理Response、这里需要用到一个小技巧。
思想是这样的:当我们调用第一个Filter处理完Request之后、应该调用下一个Filter来处理由上面一个Filter传递过来的Request、同样下一个Filter处理完之后调用下下一个来处理。直到所有Filter处理完之后开始从最后一个Filter开始处理Response、最后一个处理完之后退回上一层Filter处理Response…一直到最开始一个Filter来处理Response。这样走下来之后就会发现对Request的处理是正序的、对Response的处理是反序的。
思想有了、接下来就是具体的实现过程了。
1、首先要解决的是上一个Filter处理完之后、如何把Request、Response传递给下一个Filter?为解决这个问题、此时不由的想到FilterChain中包含了我们所有的Filter、所以我们可以在Filter定义的doFilter方法中将FilterChain传递下去。
2、那这样的话在FilterChain中就要做两件事:
a) 判断是不是还有下一个Filter、有则调用下一个的Filter的doFilter处理并继续传递、没有则返回。
b) 改变Filter的处理方式、获取下一个Filter、调用其doFilter(xxx)处理并传递参数。
3、使用的时候、我们的任务就是构造FilterChain、填充FilterChain的Filter数组、调用其doFilter方法来处理Request、Response。
4、具体代码实现:
a) 新型Filter、其doFilter方法中包含FilterChain:
package com.chy.filter.web; public interface Filter { public void doFilter(Request request, Response response, FilterChain chain); }
b) Filter的实现类HTMLFilter、SensitiveFilter、用于组装FilterChain——HTMLFilter代码:SensitiveFilter代码:
package com.chy.filter.web; public class HTMLFilter implements Filter { @Override public void doFilter(Request request, Response response, FilterChain chain) { request.setRequestStr(request.getRequestStr().replace("<", "[").replace(">", "]")+"--------HTMLFilter"); chain.doFilter(request, response, chain); response.setResponseStr(response.getResponseStr()+"--------HTMLFilter"); } } package com.chy.filter.web; public class SesitiveFilter implements Filter{ @Override public void doFilter(Request request, Response response, FilterChain chain) { request.setRequestStr(request.getRequestStr().replace("被强迫", "无奈") + "------SensitiveFilter"); chain.doFilter(request, response, chain); response.setResponseStr(response.getResponseStr()+"--------SensitiveFilter"); } }
c) FilterChain:
package com.chy.filter.web; import java.util.ArrayList; import java.util.List; public class FilterChain implements Filter{ //用于存放所有Filter List<Filter> filters = new ArrayList<Filter>(); int index = 0; //添加Filter public FilterChain addFilter(Filter f){ filters.add(f); return this; } //移除一个Filter public void removeFilter(Filter f){ filters.remove(f); } //使用这个FilterChain来过滤字符串 public void doFilter(Request request, Response response, FilterChain chain) { if(index == filters.size()){ return; } Filter f = filters.get(index); index ++; f.doFilter(request, response, chain); } }
d) Main:
package com.chy.filter.web; public class Main { public static void main(String[] args) { String msg = "大家好 :),<script>,被强迫, 敏感词汇,等等"; Request request = new Request(); request.setRequestStr(msg); Response response = new Response(); response.setResponseStr(msg); //构造第一个FilterChain FilterChain fc = new FilterChain(); fc.addFilter(new HTMLFilter()).addFilter(new SesitiveFilter()); fc.doFilter(request, response, fc); System.out.println(request.getRequestStr()); System.out.println(response.getResponseStr()); } }
运行结果:
大家好 :),[script],无奈, 敏感词汇,等等--------HTMLFilter------SensitiveFilter 大家好 :),<script>,被强迫, 敏感词汇,等等--------SensitiveFilter--------HTMLFilter可以看到:处理Request、Response的顺序是相反的。
1、总结:
到这里责任链设计模式(Chain Of Responsibility Pattern)也就告一段落了。体现了一种过滤和同时顺序处理请求和逆序处理响应的功能。struts2的Inteceptor就是以这种形式来实现的、层层拦截来达到一些功能。
2、补充:项目结构图
a) 第一个 : b)进一步: c)更近一步: