前一段时间在工作中碰到一个问题,大概就是要模拟一系列的http请求,完整一串操作。大概是首先模拟登陆,然后在管理平台界面完整一连串的增删查改,总共大概有5、6个http请求流程,最后获取到返回信息,并以接口调用的形式传给合作方接口。因为之前看过设计模式的书,所以了解到这个流程其实跟责任链流程很相似,并且中间可能会有某个流程出现改动被删除或者又要新添加一个流程,为了尽量减少变化,我决定采用责任链模式。这样的好处是显著的,第一,减少了每个http请求流程之前的耦合,上一个操作不需要知道下一个操作是什么;第二,将流程之间互相调用的关系放在了配置文件中,一旦要变动流程,只需要改动配置文件,然后实现相应的http请求操作就行了。这里,我不会把工作中的代码拿出来,因为内容较多,我就把后来自己重写的一个过滤器工具拿出来,当你不是在web环境,或者需要自定义的request的时候,没办法使用web的filter,那么我写的这个filter,可以给你提供一些帮助。
以下就是过滤器的工作流程图:
首先是过滤器接口,如果实现了他,就要自定义过滤规则
package org.loda.service;import org.loda.model.Request;import org.loda.model.Response;/** * * @ClassName: Filter * @Description: 过滤器接口 * @author minjun* */public interface Filter { public void doFilter(Request request,Response response,FilterChain chain);}
以下三个都是我自定义用来演示的过滤器实现,里面简略的写了以下过滤规则
package org.loda.demo;import org.loda.model.Request;import org.loda.model.Response;import org.loda.service.Filter;import org.loda.service.FilterChain;/** * * @ClassName: EncodingFilter * @Description: 编码过滤器 * @author minjun* */public class EncodingFilter implements Filter { @Override public void doFilter(Request request, Response response, FilterChain chain) { System.out.println("req:编码处理"); request.setAttr("encoding", "utf-8"); chain.doFilter(request, response); System.out.println("resp:编码处理"); }}
package org.loda.demo;import org.loda.model.Request;import org.loda.model.Response;import org.loda.service.Filter;import org.loda.service.FilterChain;/** * * @ClassName: MessageFilter * @Description: 信息过滤器 * @author minjun* */public class MessageFilter implements Filter { @Override public void doFilter(Request request, Response response, FilterChain chain) { System.out.println("req:数据处理"); request.setAttr("message", "处理了信息"); chain.doFilter(request, response); System.out.println("resp:数据处理"); }}
package org.loda.demo;import org.loda.model.Request;import org.loda.model.Response;import org.loda.service.Filter;import org.loda.service.FilterChain;/** * * @ClassName: PermissionFilter * @Description:权限过滤器 * @author minjun* */public class PermissionFilter implements Filter { @Override public void doFilter(Request request, Response response, FilterChain chain) { System.out.println("req:权限处理"); request.setAttr("permission", "loda"); chain.doFilter(request, response); System.out.println("resp:权限处理"); }}
上面的过滤器中会看到有三个参数
request表示请求信息,里面封装了一个map,如果设置request的值,相当于就是往map里面设置值,取值同理
response表示响应的信息,里面封装和操作与request相同
chain表示过滤器链,里面用一个集合存放了配置的过滤器,可以是很多个,按照在集合中的顺序调用。
package org.loda.service;import java.util.ArrayList;import java.util.List;import org.loda.model.Request;import org.loda.model.Response;import org.loda.util.ConfigReader;/** * * @ClassName: FilterChain * @Description: 过滤器链 * @author minjun* */public class FilterChain { //存放所有的过滤器 private Listfilters = new ArrayList (); private int index; public FilterChain() { } public FilterChain(List filters) { this.filters = filters; } /** * * @Title: readConfig * @Description: 读取配置文件中过滤器信息(可根据需求选择性调用该方法) * @param @param path 配置文件路径 * @param @return 设定文件 * @return FilterChain 返回类型 * @throws */ public FilterChain readConfig(String path){ List str=ConfigReader.read(path); for(String s:str){ try { Filter f=(Filter) Class.forName(s).newInstance(); filters.add(f); } catch (Exception e) { throw new RuntimeException("找不到这个过滤器,请检查配置文件中过滤器的配置"); } } return this; } /** * * @Title: readConfig * @Description: * 读取配置文件中过滤器信息(可根据需求选择性调用该方法) * 没有参数表示默认读取classpath下的名为filter.xml的配置文件 * @param @return * @return FilterChain 返回类型 * @throws */ public FilterChain readConfig(){ return readConfig(null); } //添加过滤器 public FilterChain addFilter(Filter filter) { filters.add(filter); return this; } //删除过滤器 public FilterChain removeFilter(Filter filter) { filters.remove(filter); return this; } //执行过滤器 public void doFilter(Request request, Response response) { //如果已经到达最后的过滤器,则返回 if(index>=filters.size()) return; //否则调用下一个过滤器 filters.get(index++).doFilter(request, response,this); }}
上述为FilterChain的实现方案,相当于用户可以手动创建FilterChain对象,然后往里面塞过滤器;观察两个可选择性调用的重载方法readConfig,可以指定配置文件名称,读取classpath下该名称的配置文件,如果没有指定,那么默认获取名为filter.xml的配置文件。也就是,如果调用了readConfig,会将配置文件里面配置的filter全部取出来并往集合里面塞。以下是配置文件标准形式:
org.loda.demo.EncodingFilter org.loda.demo.MessageFilter org.loda.demo.PermissionFilter
如上图,表示过滤器执行流程会是:EncodingFilter-->MessageFilter-->PermissionFilter。
如下是测试类,包含了使用方法:
package org.loda.test;import org.junit.Test;import org.loda.model.Request;import org.loda.model.Response;import org.loda.service.FilterChain;/** * * @ClassName: MainEntry * @Description:demo示例* @author minjun* */public class MainEntry { @Test public void test() { //创建过滤器链 FilterChain chain = new FilterChain() .readConfig()//读取配置文件(无参数表示默认配置文件路径为classpath下的filter.xml文件)// .readConfig("newFilter.xml")//手动指定配置文件的路径// .addFilter(new EncodingFilter())//添加编码过滤// .addFilter(new MessageFilter())//添加信息过滤// .addFilter(new PermissionFilter())//添加权限过滤 ; Request request = new Request().setAttr("begin", "helloword"); Response response = new Response().setAttr("begin", "helloword"); // 过滤 chain.doFilter(request, response); System.out.println("处理之后的request:\n" + request); System.out.println("处理之后的response:\n" + request); }}
整个流程是先创建过滤器链,根据情况选择是读配置文件,还是手动添加过滤器对象,或者结合使用。然后创建请求、响应对象,将数据设置进去。接着启动过滤流程。最后获取到处理信息。下面为控制台打印输出的内容,可以观察到请求时,过滤器的执行流程与我们配置的一致;响应时,执行流程反响,这与实际情况一致。并且经过过滤处理,request和response对象会有些改变。
req:编码处理req:数据处理req:权限处理resp:权限处理resp:数据处理resp:编码处理处理之后的request:message:处理了信息,encoding:utf-8,permission:loda,begin:helloword处理之后的response:message:处理了信息,encoding:utf-8,permission:loda,begin:helloword
上面代码中看到的请求响应对象打印的形式,是我自定义的toString形式,相当于获取对象中的map,然后迭代出所有属性
@Override public String toString() { StringBuilder sb = new StringBuilder(""); for (Entryentry : map.entrySet()) { sb.append(entry.getKey() + ":" + entry.getValue() + ","); } if(sb.toString().endsWith(",")){ sb.deleteCharAt(sb.length() - 1); } return sb.toString(); }
总结,其实责任链模式理解起来灰常简单,就是一个调用另外一个,如果能保证这个调用的顺序是灵活配置的,那么使用起来会非常舒服。但是实现的代码,略微有些技巧,如果有兴趣,可以看看我osgit上的源码
https://git.oschina.net/mjaow/LFilter
(以上代码部分参考了马士兵视频写的过滤器代码,其他的配置文件等附加功能的处理都是自行解决)