Filter的过滤链理解
一、Filter过滤链
web.xml配置了filter过滤器,在容器启动的时候执行了init()方法进行了初始化,然后在容器关闭的时候执行了destroy()方法销毁过滤器,在每次服务器接受请求的时候每次都会先过一遍过滤器,如果有合适的过滤器就会执行相应过滤器的doFilter方法。
doFilter方法有3个参数 ServletRequest、ServletResponse、FilterChain;前两个分别是请求和返回对象,为的是过滤后还能够进行请求或转发。FilterChain是一个过滤链,他包含了相同过滤条件的所有过滤器,例如:
类似这种相同匹配模式的过滤器会存在于同一个过滤链中,然后按照初始化的先后顺一次排列,实现逐层过滤。其中filterChain中有个doFilter方法,他的作用是将当前请求转发给过滤链中的下一个过滤器进行过滤,然后将过滤结果,只有等待下一个过滤器执行过滤完成后才能继续执行。该执行过程类似如下图:
如上图,通过过滤链逐层执行过滤就像一层嵌套,一层套一层,如果过滤链中只有一个过滤器(或者执行到最后一个)的话,执行了chain.doFilter()他会直接将请求转发出去,获取request resource资源,因为从始至终都是同一个request和response在传递,所以每次过滤都可以修改请求或返回结果,实现了过滤修改的目的。
代码实例:(只贴主要代码)
ResponseFilter.java
1 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 2 CustomResponseWrapper customResponseWrapper = new CustomResponseWrapper((HttpServletResponse) servletResponse); 3 System.out.println("ResponseFilter 执行前"); 4 filterChain.doFilter(servletRequest,customResponseWrapper);//执行下一层过滤 5 System.out.println("ResponseFilter 执行后"); 6 } 7 }
SecondFilter.java
1 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 2 System.out.println("Second doFilter执行前"); 3 filterChain.doFilter(servletRequest,servletResponse);//这是最后一层过滤器,会直接请求resource 4 System.out.println("Second doFilter执行后"); 5 }
web.xml (两个过滤器使用了相同的匹配模式‘/*’,所以会处于同一过滤链中)
1 <filter> 2 <filter-name>ResponseFilter</filter-name> 3 <filter-class>filter.ResponseFilter</filter-class> 4 </filter> 5 <filter-mapping> 6 <filter-name>ResponseFilter</filter-name> 7 <url-pattern>/*</url-pattern> 8 </filter-mapping> 9 <filter> 10 <filter-name>SecondFilter</filter-name> 11 <filter-class>filter.SecondFilter</filter-class> 12 </filter> 13 <filter-mapping> 14 <filter-name>SecondFilter</filter-name> 15 <url-pattern>/*</url-pattern> 16 </filter-mapping>
执行结果:
从执行结果并结合以上分析可以很清楚的看出doFilter的执行顺序
二、通过Filter过滤器实现对response内容的修改
CustomPrintWriter.java (重写PrintWriter方法)
1 public class CustomPrintWriter extends PrintWriter{ 2 private StringBuilder buffer; 3 4 public CustomPrintWriter(Writer out) { 5 super(out); 6 buffer = new StringBuilder(); 7 } 8 9 @Override 10 public void write(char[] buf, int off, int len) { 11 char[] dest = new char[len]; 12 System.arraycopy(buf,off,dest,0,len);//深复制字符数组 13 buffer.append(dest); 14 } 15 16 public String getContent() { 17 return buffer.toString(); 18 } 19 }
CustomResponseWrapper.java (重写HttpServletResponseWrapper方法)
1 public class CustomResponseWrapper extends HttpServletResponseWrapper{ 2 private CustomPrintWriter customPrintWriter; 3 4 public CustomResponseWrapper(HttpServletResponse response) { 5 super(response); 6 } 7 8 @Override 9 public PrintWriter getWriter() throws IOException { 10 customPrintWriter = new CustomPrintWriter(super.getWriter()); 11 return customPrintWriter; 12 } 13 14 public CustomPrintWriter getCustomPrintWriter() { 15 return customPrintWriter; 16 } 17 }
ResponseFilter.java (过滤器)
1 public class ResponseFilter implements Filter{ 2 public void init(FilterConfig filterConfig) throws ServletException {} 3 4 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 5 CustomResponseWrapper customResponseWrapper = new CustomResponseWrapper((HttpServletResponse) servletResponse); 6 filterChain.doFilter(servletRequest,customResponseWrapper);//转发请求获取请求返回结果 7 CustomPrintWriter writer = customResponseWrapper.getCustomPrintWriter();//获取请求的返回结果 8 if(writer != null){ 9 String content = writer.getContent(); 10 /** 11 * 在不修改jsp源码的情况下修改展示内容 12 */ 13 content = content.replace("XXX", LoginCheckServlet.username); 14 servletResponse.getWriter().write(content); 15 } 16 } 17 18 public void destroy() {} 19 }