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

7f59ace7a83b2b448a81e334a9ef2159.png

过滤器链设计模式

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

390a425e7cefe1dac017efc5b9a49809.png

源码讲解

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

342f2acf149803770e92d5533d09c9d3.png

tomcat的实现过滤器相关的类

8998627e11808b2c166cdf99720935bb.png

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

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

public class ApplicationFilterChain implements FilterChain {
        /**
         * Filters.
         * 过滤器数组集合,初始数组大小为0,这就意味着后面定有扩容操作
         */
        private Filter[] filters = new Filter[0];


        /**
         * The int which is used to maintain the current position
         * in the filter chain.
         * 将要执行的过滤器指针(数组下标)
         */
        private int pos = 0;




        /**
         * The int which gives the current number of filters in the chain.
         * 过滤器链上过滤器的总数量
         */
        private int n = 0;


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


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


        /**
         * The servlet instance to be executed by this chain.
         * 目标servlet,过滤器链执行完毕后直接调
         */
        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 {


            }
        }




        /**
         * Add a filter to the set of filters that will be executed in this chain.
         * 向过滤器链上注册过滤器
         * @param filterAdd The FilterConfig for the servlet to be executed
         */
        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;


        }


        /**
         * Set the servlet that will be executed at the end of this chain.
         * 设置目标servlet
         * @param servlet The Wrapper for the servlet to be executed
         */
        public void setServlet(Servlet servlet) {
            this.servlet = servlet;
        }


    }

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

2524554a406cdfd094e665d6763077dd.png

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

/**
 * @Title MyServlet
 * @Description
 * @Author lvaolin
 * @Date 2021/12/26 22:36
 **/
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--------");


    }
}

模拟调用上下文

/**
 * @Title TomcatFilterChainTest
 * @Description
 * @Author lvaolin
 * @Date 2021/12/26 12:54
 **/
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);
    }


}

运行结果

e0b6585c7d0170692677e11480a50f3a.png

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

//filterChain.doFilter(servletRequest, servletResponse);

8f577f4e034ef2f103628121b9a4eea2.png

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

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

0f7a04b757b7a94ec6a5813071ae5194.png

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吕哥架构

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值