一 简介
(1)过滤器:
依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等
关于过滤器的一些用法可以参考我写过的这些文章:
-
继承HttpServletRequestWrapper以实现在Filter中修改HttpServletRequest的参数:https://www.zifangsky.cn/677.html
-
在SpringMVC中使用过滤器(Filter)过滤容易引发XSS的危险字符:https://www.zifangsky.cn/683.html
(2)拦截器:
依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理
关于过滤器的一些用法可以参考我写过的这些文章:
-
在SpringMVC中使用拦截器(interceptor)拦截CSRF攻击(修):https://www.zifangsky.cn/671.html
-
SpringMVC中使用Interceptor+cookie实现在一定天数之内自动登录:https://www.zifangsky.cn/700.html
二 多个过滤器与拦截器的代码执行顺序
如果在一个项目中仅仅只有一个拦截器或者过滤器,那么我相信相对来说理解起来是比较容易的。但是我们是否思考过:如果一个项目中有多个拦截器或者过滤器,那么它们的执行顺序应该是什么样的?或者再复杂点,一个项目中既有多个拦截器,又有多个过滤器,这时它们的执行顺序又是什么样的呢?
下面我将用简单的代码来测试说明:
(1)先定义两个过滤器:
i)过滤器1:
1 1 [java] view plain copy 2 2 <a target="_blank" href="http://www.07net01.com/tags-package-0.html" class="infotextkey" style="background:transparent; color:rgb(66,139,202)">package</a> cn.zifangsky.filter; 3 3 4 4 import java.io.IOException; 5 5 6 6 import javax.servlet.FilterChain; 7 7 import javax.servlet.ServletException; 8 8 import javax.servlet.http.HttpServletRequest; 9 9 import javax.servlet.http.HttpServletResponse; 1010 1111 import org.springframework.web.filter.OncePerRequestFilter; 1212 1313 public class TestFilter1 extends OncePerRequestFilter { 1414 1515 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 1616 throws ServletException, IOException { 1717 //在DispatcherServlet之前执行 1818 <a target="_blank" href="http://www.07net01.com/tags-system-0.html" class="infotextkey" style="background:transparent; color:rgb(66,139,202)">system</a>.out.println("############TestFilter1 doFilterInternal executed############"); 1919 filterChain.doFilter(request, response); 2020 //在视图页面返回给<a target="_blank" href="https://my.oschina.net//u/4268457/blog/4201520/span>http://www.07net01.com/tags-%E5%AE%A2%E6%88%B7%E7%AB%AF-0.html" class="infotextkey" style="background:transparent; color:rgb(66,139,202)">客户端</a>之前执行,但是执行顺序在Interceptor之后 2121 System.out.println("############TestFilter1 doFilter after############"); 2222 // try { 2323 // Thread.sleep(10000); 2424 // } catch (InterruptedException e) { 2525 // e.printStackTrace(); 2626 // } 2727 } 2828 2929 }
ii)过滤器2:
1 1 package cn.zifangsky.filter; 2 2 3 3 import java.io.IOException; 4 4 5 5 import javax.servlet.FilterChain; 6 6 import javax.servlet.ServletException; 7 7 import javax.servlet.http.HttpServletRequest; 8 8 import javax.servlet.http.HttpServletResponse; 9 9 1010 import org.springframework.web.filter.OncePerRequestFilter; 1111 1212 public class TestFilter2 extends OncePerRequestFilter { 1313 1414 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 1515 throws ServletException, IOException { 1616 System.out.println("############TestFilter2 doFilterInternal executed############"); 1717 filterChain.doFilter(request, response); 1818 System.out.println("############TestFilter2 doFilter after############"); 1919 2020 } 2121 2222 }
iii)在web.xml中注册这两个过滤器:
1 1 <!-- 自定义过滤器:testFilter1 --> 2 2 <filter> 3 3 <filter-name>testFilter1</filter-name> 4 4 <filter-class>cn.zifangsky.filter.TestFilter1</filter-class> 5 5 </filter> 6 6 <filter-mapping> 7 7 <filter-name>testFilter1</filter-name> 8 8 <url-pattern>/*</url-pattern> 9 9 </filter-mapping> 1010 <!-- 自定义过滤器:testFilter2 --> 1111 <filter> 1212 <filter-name>testFilter2</filter-name> 1313 <filter-class>cn.zifangsky.filter.TestFilter2</filter-class> 1414 </filter> 1515 <filter-mapping> 1616 <filter-name>testFilter2</filter-name> 1717 <url-pattern>/*</url-pattern> 1818 </filter-mapping>
(2)再定义两个拦截器:
i)拦截器1,基本拦截器:
1 1 package cn.zifangsky.interceptor; 2 2 3 3 import javax.servlet.http.HttpServletRequest; 4 4 import javax.servlet.http.HttpServletResponse; 5 5 6 6 import org.springframework.web.servlet.HandlerInterceptor; 7 7 import org.springframework.web.servlet.ModelAndView; 8 8 9 9 public class BaseInterceptor implements HandlerInterceptor{ 1010 1111 /** 1212 * 在DispatcherServlet之前执行 1313 * */ 1414 public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { 1515 System.out.println("************BaseInterceptor preHandle executed**********"); 1616 return true; 1717 } 1818 1919 /** 2020 * 在controller执行之后的DispatcherServlet之后执行 2121 * */ 2222 public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) 2323 throws Exception { 2424 System.out.println("************BaseInterceptor postHandle executed**********"); 2525 } 2626 2727 /** 2828 * 在页面渲染完成返回给客户端之前执行 2929 * */ 3030 public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) 3131 throws Exception { 3232 System.out.println("************BaseInterceptor afterCompletion executed**********"); 3333 // Thread.sleep(10000); 3434 } 3535 3636 }
ii)指定controller请求的拦截器:
1 1 package cn.zifangsky.interceptor; 2 2 3 3 import javax.servlet.http.HttpServletRequest; 4 4 import javax.servlet.http.HttpServletResponse; 5 5 6 6 import org.springframework.web.servlet.HandlerInterceptor; 7 7 import org.springframework.web.servlet.ModelAndView; 8 8 9 9 public class TestInterceptor implements HandlerInterceptor { 1010 1111 public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { 1212 System.out.println("************TestInterceptor preHandle executed**********"); 1313 return true; 1414 } 1515 1616 public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) 1717 throws Exception { 1818 System.out.println("************TestInterceptor postHandle executed**********"); 1919 } 2020 2121 public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) 2222 throws Exception { 2323 System.out.println("************TestInterceptor afterCompletion executed**********"); 2424 } 2525 }
iii)在SpringMVC的配置文件中注册这两个拦截器:
1 1 <!-- 拦截器 --> 2 2 nbsp; <mvc:interceptors> 3 3 <!-- 对所有请求都拦截,公共拦截器可以有多个 --> 4 4 <bean name="baseInterceptor" class="cn.zifangsky.interceptor.BaseInterceptor" /> 5 5 <!-- <bean name="testInterceptor" class="cn.zifangsky.interceptor.TestInterceptor" /> --> 6 6 <mvc:interceptor> 7 7 <!-- 对/test.html进行拦截 --> 8 8 <mvc:mapping path="/test.html"/> 9 9 <!-- 特定请求的拦截器只能有一个 --> 1010 <bean class="cn.zifangsky.interceptor.TestInterceptor" /> 1111 </mvc:interceptor> 1212 </mvc:interceptors>
(3)定义一个测试使用的controller:
1 1 package cn.zifangsky.controller; 2 2 3 3 import org.springframework.stereotype.Controller; 4 4 import org.springframework.web.bind.annotation.RequestMapping; 5 5 import org.springframework.web.servlet.ModelAndView; 6 6 7 7 @Controller 8 8 public class TestController { 9 9 1010 @RequestMapping("/test.html") 1111 public ModelAndView handleRequest(){ 1212 System.out.println("---------TestController executed--------"); 1313 return new ModelAndView("test"); 1414 } 1515 }
(4)视图页面test.jsp:
1 1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 2 pageEncoding="UTF-8"%> 3 3 <% 4 4 String path = request.getContextPath(); 5 5 String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; 6 6 %> 7 7 <html> 8 8 <head> 9 9 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 1010 <base href="http://983836259.blog.51cto.com/7311475/"> 1111 <title>FilterDemo</title> 1212 </head> 1313 <body> 1414 <% 1515 System.out.println("test.jsp is loading"); 1616 %> 1717 <div align="center"> 1818 This is test page 1919 </div> 2020 </body> 2121 </html>
(5)测试效果:
启动此测试项目,可以看到控制台中输出如下:

这就说明了过滤器的运行是依赖于servlet容器的,跟springmvc等框架并没有关系。并且,多个过滤器的执行顺序跟xml文件中定义的先后关系有关
接着清空控制台中的输出内容并访问:http://localhost:9180/FilterDemo/test.html
可以看到,此时的控制台输出结果如下:

相信从这个打印输出,大家就可以很清晰地看到有多个拦截器和过滤器存在时的整个执行顺序了。当然,对于过个拦截器它们之间的执行顺序跟在SpringMVC的配置文件中定义的先后顺序有关
注:对于整个SpringMVC的执行流程来说,如果加上上面的拦截器和过滤器,其最终的执行流程就如下图所示:
