zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    JS缓存图片实例
    Windows Server 2008上安装Media Player
    [转] BizTalk Server 2010新功能介绍(一):概述
    Microsoft BizTalk ESB Toolkit 2.0
    Asp.NET导出Excel文件乱码解决若干方法
    [PM Tools]软件项目进度跟踪表v3.0
    关于Silverlight中多项目共享DLL文件的讨论
    Silverlight中的ListBox横向显示CheckBox
    设计模式.简单工厂
    Silverlight用户控件转移时产生的“元素已经是另一个元素的子元素”问题
  • 原文地址:https://www.cnblogs.com/IC1101/p/14340320.html
Copyright © 2011-2022 走看看