代码精进之路-设计模式-过滤器链模式

代码精进之路-设计模式-过滤器链模式,第1张

代码精进之路-设计模式-过滤器链模式

过滤器链设计模式

Java Web应用开发中过滤器Filter都用过,今天老吕带大家来看看最常用的tomcat中是如何实现 Servlet规范中过滤器链功能的。

源码讲解

javax.servlet包中关于过滤器的接口

tomcat的实现过滤器相关的类

主要看这个ApplicationFilterChain类,它实现了FilterChain接口,是关键所在。

为了便于理解代码,我把tomcat源码中和设计模式无关的代码都清理了,只保留下最关键的代码,并加了注释,个别依赖的类也进行了简单处理。

public class ApplicationFilterChain implements FilterChain {
        
        private Filter[] filters = new Filter[0];


        
        private int pos = 0;




        
        private int n = 0;


        // --------------------------------------------------------------


        //过滤器数组扩容容量增量值
        public static final int INCREMENT = 10;


        
        private Servlet servlet = null;


        @Override
        public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
            internalDoFilter(request, response);
        }


        private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {


            // Call the next filter if there is one
            if (pos < n) {
                //继续执行下一个过滤器,注意pos的++ 操作,这个是过滤器链指针指向了下一个过滤器,为下一个过滤器的执行做好准备
                Filter filter = filters[pos++];
                try {
                    //最后一个参数是关键
                    //这里面并没有使用for循环把所有的过滤器调用一遍,
                    //而是用了一个递归操作,通过传递当前FilterChain实例,将调用下下一个过滤器的决定权交给了下一个过滤器


                    filter.doFilter(request, response, this);
                } catch (IOException | ServletException | RuntimeException e) {
                    throw e;
                } catch (Throwable e) {
                    throw new ServletException("filterChain.filter", e);
                }
                //如果下一个过滤器忘记了向下传递,就会走到这里,意味着请求的中断
                //再也不会调到目标servlet了  
                return;
            }


            // We fell off the end of the chain -- call the servlet instance
            try {
                //出了 过滤器链,向下调用servlet实例方法
                servlet.service(request, response);
            } catch (IOException | ServletException | RuntimeException e) {
                throw e;
            } catch (Throwable e) {
                throw new ServletException("filterChain.servlet", e);
            } finally {


            }
        }




        
        void addFilter(Filter filterAdd) {


            // Prevent the same filter being added multiple times
            //防止添加重复的过滤器
            for (Filter filter : filters) {
                if (filter == filterAdd) {
                    return;
                }
            }




            //过滤器数组已满,进行扩容操作,默认新增10个容量
            if (n == filters.length) {
                Filter[] newFilters =
                        new Filter[n + INCREMENT];
                System.arraycopy(filters, 0, newFilters, 0, n);
                filters = newFilters;
            }
            //将新增的过滤器追加到过滤器链上,注意n++ ,它代表了当前实际存储的过滤器个数,因为数组不一定能填满,所以n来记录是有必要的
            filters[n++] = filterAdd;


        }


        
        public void setServlet(Servlet servlet) {
            this.servlet = servlet;
        }


    }

接下来我就以上面的ApplicationFilterChain为基础来写个demo测试下这个过滤器链

Filter1、Filter2、Filter3、FilterResponseTime 为4个过滤器,其中最后一个实现了请求耗时统计功能

四个过滤器

public class Filter1 implements Filter {


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("过滤器1");
        //能调用下一个过滤器的关键,如果没有这一句,将中断(跳出)过滤器链
        filterChain.doFilter(servletRequest, servletResponse);
    }


    @Override
    public void destroy() {}
}


public class Filter2 implements Filter {


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("过滤器2");
        //能调用下一个过滤器的关键,如果没有这一句,将中断(跳出)过滤器链
        filterChain.doFilter(servletRequest, servletResponse);
    }


    @Override
    public void destroy() {}
}


public class Filter3 implements Filter {


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("过滤器3");
        //能调用下一个过滤器的关键,如果没有这一句,将中断(跳出)过滤器链
        filterChain.doFilter(servletRequest, servletResponse);
    }


    @Override
    public void destroy() {}
}




//实现一个接口耗时统计功能
public class FilterResponseTime implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {


    }


    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        long start = System.currentTimeMillis();
        try {
            filterChain.doFilter(servletRequest, servletResponse);
        } finally {
            long timeCost = System.currentTimeMillis() - start;
            System.out.println("耗时" + timeCost + "ms");
        }


    }




    @Override
    public void destroy() {
    }
}

模拟一个目标Servlet

public class MyServlet extends HttpServlet {


    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(" -----到达 servlet service--------");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(" -----结束 servlet service--------");


    }
}

模拟调用上下文

public class Main {
    public static void main(String[] args) throws IOException, ServletException {
        //准备过滤器链
        ApplicationFilterChain filterChain = new ApplicationFilterChain();
        //设置目标servlet
        filterChain.setServlet(new MyServlet());
        //设置过滤器集合
        filterChain.addFilter(new Filter1());
        filterChain.addFilter(new Filter2());
        filterChain.addFilter(new Filter3());
        filterChain.addFilter(new FilterResponseTime());
        //过滤请求
        System.out.println("-----request 开始----");
        filterChain.doFilter(null, null);
    }


}

运行结果

在过滤器2中中断传递后的效果

//filterChain.doFilter(servletRequest, servletResponse);

发现请求中断了,不能达到servlet了,

原因就在老吕的ApplicationFilterChain代码注释中,去看看吧。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5683608.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存