责任链模式和自定义过滤器链

3,347 阅读3分钟

责任链模式和自定义过滤器链

什么是责任链模式:

使多个对象都有机会处理请求,从而避免请求的发送着和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

通俗理解:责任链模式就像是一条产品生产流水线,每个生产车间处理过后才会把该产品流到下一个生产车间。 如图:

1.png

java里面的责任链设计模式类图:

2.png

案例分析1:

现在有一个集合,对应三个处理方法,这三个方法各自判断集合中是否存在当前方法执行的条件,如果有则输出如果没有适合自己处理的条件则把当前请求传递下去。

以下是实现的代码片段:

抽象父类:

public abstract class Hanler {
    protected Hanler successor;
    public void SetSuccessor(Hanler successor){
        this.successor=successor;
    }
    // 处理请求的方法,需要子类重写
    public abstract void HandlerRequest(int request);
}

责任链上的三个角色:

ConcreteHandler1

public class ConcreteHandler1 extends Hanler {
    @Override
    public void HandlerRequest(int request) {
        if(request>=0 && request<10){
            System.out.println("ConcreteHandler1处理请求"+request);
            // 其他逻辑
        }
        else if(successor!=null){
            // 转移到下一位
            successor.HandlerRequest(request);
        }
    }
}

ConcreteHandler2:

public class ConcreteHandler2 extends Hanler {
    @Override
    public void HandlerRequest(int request) {
        if(request>=10 && request<20){
            System.out.println("ConcreteHandle2处理请求"+request);
            // 其他逻辑
        }
        else if(successor!=null){
            // 转移到下一位
            successor.HandlerRequest(request);
        }
    }
}

ConcreteHandler3:

public class ConcreteHandler3 extends Hanler {
    @Override
    public void HandlerRequest(int request) {
        if(request>=20 && request<30){
            System.out.println("ConcreteHandler3处理请求"+request);
             // 其他逻辑
        }
        else if(successor!=null){
            // 转移到下一位
            successor.HandlerRequest(request);
        }
    }
}

main方法:把各个实现类串联起来形成一条“链”,如下:

    public static void main(String[] args) {
        Hanler h1 = new ConcreteHandler1();
        Hanler h2 = new ConcreteHandler2();
        Hanler h3 = new ConcreteHandler3();
        // 将各个实现类串联
        h1.SetSuccessor(h2);
        h2.SetSuccessor(h3);

        int[] requests={0,15,20,62};
        for(int i=0;i<requests.length;i++){
            h1.HandlerRequest(requests[i]);
        }
    }

测试结果为:

3.png

相信这几个代码片段大家理解起来应该不是问题啦,责任链模式就是这么简单。

责任链模式案例2

如果有使用过SpringSecurity的同学对责任链模式应该是很熟悉了,其原理设计也是使用了责任链模式,以下我们根据Filter过滤器自定义一个简易版的责任链模式。

基于Filter实现过滤器链版本1

实现思路: 新建多个类实现Filter接口,通过@Order注解指定各个实现类的执行顺序:

过滤器1: MyFilter1 获取请求地址

@Component
@Order(1)
public class MyFilter1 implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

      HttpServletRequest request=(HttpServletRequest) servletRequest;
      System.out.println("执行MyFilter1当前请求地址==" + request.getRequestURL());
      filterChain.doFilter(servletRequest,servletResponse);
    }
}

过滤器2:获取请求方式

@Component
@Order(2)
public class MyFilter2 implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
      HttpServletRequest request=(HttpServletRequest) servletRequest;
      System.out.println("执行MyFilter2当前请求方式==" + request.getMethod());
      filterChain.doFilter(servletRequest,servletResponse);
    }
}

过滤器3:获取请求请求URI

@Component
@Order(3)
public class MyFilter3 implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
      HttpServletRequest request=(HttpServletRequest) servletRequest;
      System.out.println("执行MyFilter3当前请求URI==" + request.getRequestURI());
      filterChain.doFilter(servletRequest,servletResponse);
    }
}

运行结果如图:

4.png

在以上每个过滤器上都有@Order() 这个注解,而且我们默认给的值从第一个过滤器到第三个过滤器依次递增,结果自然也是依次执行所有过滤器。

修改过滤器上的@Order()值,从MyFilter3-MyFilter1执行如图:

5.png

可以看到MyFilter3是第一个执行,MyFilter1是最后一个执行了!

通过以上例子我们可以知道可以通过@Order()这个注解的属性值大小来定义过滤器的执行顺序。以上版本我们是每个过滤器都去实现Filter接口重写其方法,然后通过@Order()值的大小来给这些过滤器排序。

基于Filter实现过滤器链版本2

设计思路:过滤器链上的过滤器大可不必每个都实现Filter接口,我们可以把所有的过滤器保持到一个集合里面,由一个过滤器去调用执行集合里面的过滤器即可。

添加MyFilter接口:

/**
 * 不必实现Filter接口
 */
public interface MyFilter {
     // 由子类从写方法
     Boolean doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;
}

MyFilter1: 去掉@Component 和@Order注解,通过返回 true 或者false 控制拦截或者 放行

public class MyFilter1 implements MyFilter {
    @Override
    public Boolean doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request=(HttpServletRequest) servletRequest;
        System.out.println("执行MyFilter1当前请求地址==" + request.getRequestURL());
        // 通过返回 true 或者false 控制拦截或者 放行
        return true;
    }
}

MyFilter2:

public class MyFilter2  implements MyFilter {
    @Override
    public Boolean doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request=(HttpServletRequest) servletRequest;
        System.out.println("执行MyFilter2当前请求方式==" + request.getMethod());
       return true;
    }
}

MyFilter3:

public class MyFilter3 implements MyFilter {
    @Override
    public Boolean doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request=(HttpServletRequest) servletRequest;
        System.out.println("执行MyFilter3当前请求URI==" + request.getRequestURI());
        return true;
    }
}

定义一个过滤器控制类MyFilterContainer实现Filter,用来执行过滤器,这里是关键点:

@Component
public class MyFilterContainer implements Filter {
    //存储过滤器
   public List<MyFilter> filterList=new ArrayList<>();
   // 当前执行的过滤器
   private Integer current=0;
   
   // 项目启动时扫描时会执行addFilter()方法,会把所有过滤器存入到 集合中
    @Bean
    public void addFilter(){
        filterList.add(new MyFilter1());
        filterList.add(new MyFilter2());
        filterList.add(new MyFilter3());
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //保证每次请求都从第一个拦截器开始
        this.current=0;
        //获取下一个过滤器并执行
        Boolean isDoNext= doMyFilter(servletRequest, servletResponse, filterChain);
        if(!isDoNext){
            // 返回结果为false 则拦截当前请求
           return;
       }
       // 放行
       filterChain.doFilter(servletRequest,servletResponse);
    }
    // 获取下一个过滤器并执行
    private Boolean doMyFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //根据当前索引获取执行的过滤器对象
        MyFilter myFilter = filterList.get(this.current);
        Boolean aBoolean = myFilter.doFilter(servletRequest, servletResponse, filterChain);
        if(aBoolean && this.current<this.filterList.size()-1){
            this.current=this.current+1;
            //递归调用下一个过滤器对象
            return doMyFilter(servletRequest, servletResponse, filterChain);
        } else if(aBoolean && this.current==this.filterList.size()-1){
            return true;
        }else {
            return false;
        }
    }
}

启动项目测试结果如下图:

6.png

过滤器执行的顺序是由该过滤器处于集合的顺序而定,切换为以下顺序

@Bean
    public void addFilter(){
        filterList.add(new MyFilter3());
        filterList.add(new MyFilter1());
        filterList.add(new MyFilter2());
       
    }

运行结果如图:

7.png

拦截规则配置测试

现在有个接口:

@RestController
public class FilterController {
    @GetMapping("test")
    public String test(){
        return "hello filter";
    }
}

当请求为/test的 的时候放行,否则拦截。只需要简单需改 MyFilter3即可 如下:

public class MyFilter3 implements MyFilter {
    @Override
    public Boolean doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request=(HttpServletRequest) servletRequest;
        System.out.println("执行MyFilter3当前请求URI==" + request.getRequestURI());
        
        //当请求为/test的 的时候放行,否则拦截
        if(!request.getRequestURI().equals("/test")){
            System.out.println("MyFilter3拦截请求,当前请求URI==" + request.getRequestURI());
            return false;
        }
        return true;
    }
}

运行测试:

8.png

从上图中可以看出测试结果和我们所需要的是一致的,至于返回拦截信息那也是很容易扩展的了,这里不再演示。至此本篇内容完结啦!!!

最后

本篇文章从责任链设计模式讲起,相信通过本篇学习大家也应该掌握了责任链模式、过滤器等知识。