过滤器链设计模式
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代码注释中,去看看吧。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)