Filter Pattern

1.什么是Filter Pattern?

    Filter Pattern 也可以叫Intercepting Filter Pattern(拦截过滤器模式),这个范式可以让你在执行原有逻辑(核心逻辑)之前和之后额外执行一系列逻辑,像这样:
Filter Pattern实例1Filter Pattern实例2





2.这个范式能给我们的代码带来什么好处?

    Filter Pattern最广为人知的应用莫过于web应用中的servlet filter的,通过filter我们可以实现鉴权逻辑(哪些请求可以被执行下去,哪些不可以),access日志输出(对每个请求都输出日志参数)等。这些filter可以让系统在不修改核心业务逻辑的基础上,按照一定顺序添加切面功能,也就是Aspect Oriented Programming,实现功能的解耦合。





3.如何设计一个Filter Pattern?

    首先任何设计模式都离不开应用场景,这里我们我们假定一个典型的http请求响应的逻辑:


public class HttpServlet implements Servlet{

    @Override
    public Response service(Request request){
        Response response = new Response();

        response.setData( "RESPONSE OF " + request.getData());

        return response;
    }
}

     此时想在这个service逻辑上层添加print request的逻辑,以及modify request的逻辑,粗放的写法象这样:


public class HttpServlet implements Servlet{

    @Override
    public Response service(Request request){
        System.out.println(request.getData());

        request.setData("modify " + request.getData());        

        Response response = new Response();

        response.setData( "RESPONSE OF " + request.getData());

        return response;
    }
}

     这样当然能实现功能,但是代码的可维护性,可读性都会变得很差;于是把print 和 modify功能单独抽取出来like this;


public class HttpServlet implements Servlet{


    /**
     * 执行filterList,并执行service
     * @param request
     * @return
     */
    public Response doFilter(Request request) {
        List<SimpleFilter> simpleFilterList = buildFilterList();

        for (SimpleFilter simpleFilter : simpleFilterList) {
            simpleFilter.doBefore(request);
        }

        Response response = this.service(request);

        for (SimpleFilter simpleFilter : simpleFilterList) {
            simpleFilter.doAfter(response);
        }

        return response;

    }


    /**
     * 构建Filter List
     */
    private List<SimpleFilter> buildFilterList() {
        List<SimpleFilter> filterList = new ArrayList<>();

        SimpleFilter printFilter = new SimpleFilter() {
            @Override
            public void doBefore(Request request) {
                System.out.println(request);
            }

            @Override
            public void doAfter(Response response) {
                //do noting
            }
        };

        SimpleFilter modifyFilter = new SimpleFilter() {
            @Override
            public void doBefore(Request request) {
                request.setData("modify " + request.getData());
            }

            @Override
            public void doAfter(Response response) {
                //do noting
            }
        };

        filterList.add(printFilter);
        filterList.add(modifyFilter);
        return filterList;
    }


    @Override
    public Response service(Request request){

        Response response = new Response();

        response.setData( "RESPONSE OF " + request.getData());

        return response;
    }


    public interface SimpleFilter {

        void doBefore(Request request);


        void doAfter(Response response);
    }
}

    这里实现的逻辑和HandlerInterceptor的思想很像,有兴趣的话可以看看org.springframework.web.servlet.HandlerInterceptor相关的实现,都是在核心代码的前后循环调用interceptor的函数;
但是这里有一个问题,当前版本的代码,SimpleFilterdoBefore方法无法控制流程是否往下走,无法实现类似流控,鉴权相关的功能;当然硬要在此基础上去改也能改出来,只不过不是很优雅,下面尝试另一个东西FilterChain

4.FilterChain

    FilterChain顾名思义,肯定包含一个Filter List并且是用过addFilter方法构建的, 有一个返回参数为ResponsedoFilter方法,看来是核心入口;看看它的实现类:


public interface FilterChain {


    Response doFilter(Request request);


    void addFilter(Filter filter);
}



public class ApplicationFilterChain implements FilterChain {

    private List<Filter> filters;

    //当前执行待执行filter的位置
    private int position;

    private Servlet servlet;

    public ApplicationFilterChain() {
        filters = new LinkedList<>();
        position = 0;
        servlet = new HttpServlet();
    }

    @Override
    public Response doFilter(Request request) {
        Filter currentFilter;
        if (position < filters.size()){
            currentFilter = filters.get(position);
            position ++;
            //顺序执行filter list
            return currentFilter.doFilter(this, request);
        }else {
            //执行真正的service方法
            return servlet.service(request);
        }
    }

    @Override
    public void addFilter(Filter filter) {
        this.filters.add(filter);
    }

}

     ApplicationFilterChain 的代码总体符合猜想,但是多了servlet,position属性,position用来定位执行filter的执行位置,servlet放的是核心业务逻辑;
但是doFilter又是串起所有filter和servlet的呢?看一下新的Filter接口参数,以及实现类:


public interface Filter {

    Response doFilter(FilterChain filterChain, Request request);
}

public class PrintFilter implements Filter {

    @Override
    public Response doFilter(FilterChain filterChain, Request request) {
        //do my filter
        System.out.println("realFilter.doFilter: " + request.getData());
        return filterChain.doFilter(request);
    }
}

public class ModifyFilter implements Filter {

    @Override
    public Response doFilter(FilterChain filterChain, Request request) {
        request.setData("modified " + request.getData());
        return filterChain.doFilter(request);
    }
}

    doFilter签名是FilterChainRequest,这里的FilterChain的用法是filterChain.doFilter(request),是用来串起所有filter list;
所以最后的代码执行时序是这样的(算是套了两个函数的递归调用)

filterChain.doFilter -> filter.doFilter -> filterChain.doFilter -> filter.doFilter -> filterChain.doFilter -> servlet.service

    Main方法如下:

public class Main {

    public static void main(String[] args) {


        FilterChain filterChain = buildFilterChain();


        Response response = filterChain.doFilter(new Request("request"));


        System.out.println(response.getData());
    }





    /**
     * 构建filterChain
     * @return
     */
    private static FilterChain buildFilterChain() {
        FilterChain filterChain = new ApplicationFilterChain();

        //修改请求数据filter
        Filter modifyDataFilter = new ModifyFilter();

        //打印请求数据filter
        PrintFilter printFilter = new PrintFilter();

        filterChain.addFilter(modifyDataFilter);

        filterChain.addFilter(printFilter);

        return filterChain;
    }
}

     debug的线程栈如下图
在servlet打断点时的线程栈

    在这个范式里面主要是FilterChain和Filter两个类的doFilter方法实现逻辑的串联,FilterChain控制每次执行第几个Filter,而Filter执行filter逻辑,并调用FilterChain去执行下一个Filter,如果这个Filter没有调用FilterChain而是直接return了,那么链路就会从当前Filter返回回去。
UML类图

5.参考文献

runoob-拦截过滤器模式
baeldung-Intercepting Filter Pattern In Java

posted on 2021-01-28 21:06  mindSucker  阅读(97)  评论(0编辑  收藏  举报