责任链模式
1. 责任链模式 (Chain of Responsibility Pattern)
在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任
本博文以JavaWeb的Filter过滤器为例来类比说明,不了解过滤器的同学可以 康康这里。来复习一下过滤器的工作流程:
客户端发出请求,请求来到web服务器,然后经由过滤器,到达资源
然后就到响应了,响应来到过滤器,再经由web服务器,最后到达客户端,流程图如下:
使用过滤器的常规流程(继承Filter接口,一般重写里面的doFilter方法就可以了,然后在web.xml里配置)
public class EncodingFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 对请求进行处理
// 表示放行,去下一个过滤器
chain.doFilter(request, response);
// 对响应进行处理
}
}
2. 实现
至此我们已经初步了解过滤器的基本使用了,这就是使用了责任链模式,主要包括的对象有:
- 请求、响应(Req、Resp)
- 过滤器接口(Filter)
- 过滤器链(FilterChain)
- 过滤器实现类(MyFilter)
那么我们下面开始说明责任链模式(以一个请求响应为例)
2.1 请求响应对象
public class Request{
// 模拟request请求对象,实际上没有这么简单
// 这里用字符串表示请求内容
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Request(String content) {
this.content = content;
}
}
public class Response{
// 模拟response响应对象,实际上没有这么简单
// 这里用字符串表示响应内容
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Response(String content) {
this.content = content;
}
}
2.2 新建自己的Filter接口(与javaweb的过滤器相似,为了类比而自己创建的)
public interface Filter {
public void doFilter(Request request, Response response, FilterChain filterChain);
}
2.3 过滤器链
class FilterChain {
private List<Filter> list = new ArrayList<>();
private int index = 0;
public FilterChain add(Filter filter){
this.list.add(filter);
return this;
}
public void doFilter(Request request, Response response, FilterChain filterChain) {
if(index == list.size()){
// 请求通过了所有过滤器,可以访问资源了
// 这里模拟Servlet处理请求后返回响应为 "BBBB"
String result = " BBBB ";
response.setContent(result);
// 处理完请求后就返回
return ;
}
Filter filter = list.get(index);
index++;
filter.doFilter(request, response, filterChain);
}
}
2.4 过滤器接口实现类
class MyFilterOne implements Filter{
@Override
public void doFilter(Request request, Response response, FilterChain filterChain) {
request.setContent(request.getContent().trim()); // 这里处理请求,作用是去除两端空格
filterChain.doFilter(request, response, filterChain);
response.setContent(response.getContent().trim()); // 这里处理响应,作用是去除两端空格
}
}
class MyFilterTwo implements Filter{
@Override
public void doFilter(Request request, Response response, FilterChain filterChain) {
request.setContent(request.getContent().replace('A', 'B')); // 这里处理请求,作用是替换字符
filterChain.doFilter(request, response, filterChain);
request.setContent(request.getContent().replace('B', 'A')); // 这里处理响应,作用是替换字符
}
}
2.5 模拟请求响应过程
// 没有注释的都是由Tomcat创建
public static void main(String[] args) {
Request request = new Request(" AAAAA ");
Response response = new Response();
FilterChain filterChain = new FilterChain();
MyFilterOne myFilterOne = new MyFilterOne();// 我们新建的继承类,由Tomact加载web.xml时创建
MyFilterTwo myFilterTwo = new MyFilterTwo();// 我们新建的继承类,由Tomact加载web.xml时创建
filterChain.add(myFilterOne).add(myFilterTwo);
filterChain.doFilter(request, response, filterChain);
System.out.println(response.getContent()); // 模拟响应给浏览器
}
请求访问到资源了
AAAA
3. 看看javaWeb的Filter相关类
Filter
public interface Filter {
public default void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
public default void destroy() {}
}
FilterChain,看其源码发现是个接口,其实下面的类才是真正的调用者,这里使用了向上转型
public interface FilterChain {
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
}
ApplicationFilterChain,继承了FilterChain,找的我好辛苦,被Tomat创建
// 用了pos来记录当前Filter位置
private int pos = 0;
// 内部使用了数组来存Filter
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
// 一些代码
}else {
internalDoFilter(request,response);
}
}
// 代码好复杂,省略了大部分,留下了主干
private void internalDoFilter(ServletRequest request,ServletResponse response) throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
if( Globals.IS_SECURITY_ENABLED ) {
// 一些代码
} else {
filter.doFilter(request, response, this);
}
return;
}
}