Spring 过滤器和拦截器的区别和联系
一 简介
(1)过滤器(Filter):
依赖于servlet容器,是JavaEE标准,是在请求进入容器之后,还未进入Servlet之前进行预处理,并且在请求结束返回给前端这之间进行后期处理。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作。过滤器可以简单理解为“取你所想取”,忽视掉那些你不想要的东西,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。过滤器底层实现方式是基于函数回调的,自定义过滤器实现一个 doFilter()方法(init()和destroy()方法可以不实现,有默认实现),这个方法有一个FilterChain 参数,而实际上它是一个回调接口,基于函数回调实现。ApplicationFilterChain是它的实现类, 这个实现类内部也有一个 doFilter() 方法就是回调方法。过滤器Filter触发时机是在请求进入容器后,但在进入servlet(StandWrapper类)之前进行预处理,请求结束是在servlet(StandWrapper类)处理完以后。也可以通过@WebFilter注解实现对特定url拦截。
关于过滤器的一些用法可以参考我写过的这些文章:
-
继承HttpServletRequestWrapper以实现在Filter中修改HttpServletRequest的参数:https://www.zifangsky.cn/677.html
-
在SpringMVC中使用过滤器(Filter)过滤容易引发XSS的危险字符:https://www.zifangsky.cn/683.html
(2)拦截器:
拦截器不依赖与servlet容器,依赖于web框架。一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application、Swing等程序中。在SpringMVC中就是依赖于SpringMVC框架,在SSH框架中,就是依赖于Struts框架。在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用spring的依赖注入(DI)获取IOC容器中的各个bean,进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,即⑴请求还没有到controller层时进行拦截,⑵请求走出controller层次,还没有到渲染时图层时进行拦截,⑶结束视图渲染,但是还没有到servlet的结束时进行拦截。对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理,拦截器功在对请求权限鉴定方面确实很有用处。它可以简单理解为“拒你所想拒”。拦截器底层实现方式是基于Java的反射机制(动态代理)实现的。拦截器Interceptor触发时机是在请求进入servlet(StandWrapper类)后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。拦截器通过实现HandlerInterceptor接口来实现的,有三个方法preHandle(),postHandle(),afterCompletion()。拦截器是基于Java的反射机制实现,也就是动态代理。
关于过滤器的一些用法可以参考我写过的这些文章:
-
在SpringMVC中使用拦截器(interceptor)拦截CSRF攻击(修):https://www.zifangsky.cn/671.html
-
SpringMVC中使用Interceptor+cookie实现在一定天数之内自动登录:https://www.zifangsky.cn/700.html
二 多个过滤器与拦截器的代码执行顺序
如果在一个项目中仅仅只有一个拦截器或者过滤器,那么我相信相对来说理解起来是比较容易的。但是我们是否思考过:如果一个项目中有多个拦截器或者过滤器,那么它们的执行顺序应该是什么样的?或者再复杂点,一个项目中既有多个拦截器,又有多个过滤器,这时它们的执行顺序又是什么样的呢?
下面我将用简单的代码来测试说明:
(1)先定义两个过滤器
a. 过滤器1
<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; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.filter.OncePerRequestFilter; public class TestFilter1 extends OncePerRequestFilter { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { //在DispatcherServlet之前执行 <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############"); filterChain.doFilter(request, response); //在视图页面返回给<a target="_blank" href="http://www.07net01.com/tags-%E5%AE%A2%E6%88%B7%E7%AB%AF-0.html" class="infotextkey" >客户端</a>之前执行,但是执行顺序在Interceptor之后 System.out.println("############TestFilter1 doFilter after############"); // try { // Thread.sleep(10000); // } catch (InterruptedException e) { // e.printStackTrace(); // } } }
b. 过滤器2
package cn.zifangsky.filter; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.filter.OncePerRequestFilter; public class TestFilter2 extends OncePerRequestFilter { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { System.out.println("############TestFilter2 doFilterInternal executed############"); filterChain.doFilter(request, response); System.out.println("############TestFilter2 doFilter after############"); } }
c. 在web.xml中注册这两个过滤器
<!-- 自定义过滤器:testFilter1 --> <filter> <filter-name>testFilter1</filter-name> <filter-class>cn.zifangsky.filter.TestFilter1</filter-class> </filter> <filter-mapping> <filter-name>testFilter1</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 自定义过滤器:testFilter2 --> <filter> <filter-name>testFilter2</filter-name> <filter-class>cn.zifangsky.filter.TestFilter2</filter-class> </filter> <filter-mapping> <filter-name>testFilter2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
(2)再定义两个拦截器
a. 拦截器1,基本拦截器:
c. 在SpringMVC的配置文件中注册这两个拦截器:
<!-- 拦截器 --> nbsp; <mvc:interceptors> <!-- 对所有请求都拦截,公共拦截器可以有多个 --> <bean name="baseInterceptor" class="cn.zifangsky.interceptor.BaseInterceptor" /> <!-- <bean name="testInterceptor" class="cn.zifangsky.interceptor.TestInterceptor" /> --> <mvc:interceptor> <!-- 对/test.html进行拦截 --> <mvc:mapping path="/test.html"/> <!-- 特定请求的拦截器只能有一个 --> <bean class="cn.zifangsky.interceptor.TestInterceptor" /> </mvc:interceptor> </mvc:interceptors>
(3)定义一个测试使用的controller
(4)视图页面test.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <base href="http://983836259.blog.51cto.com/7311475/"> <title>FilterDemo</title> </head> <body> <% System.out.println("test.jsp is loading"); %> <div align="center"> This is test page </div> </body> </html>
(5)测试效果
启动此测试项目,可以看到控制台中输出如下:
这就说明了过滤器的运行是依赖于servlet容器的,跟springmvc等框架并没有关系。并且,多个过滤器的执行顺序跟xml文件中定义的先后关系有关
接着清空控制台中的输出内容并访问:http://localhost:9180/FilterDemo/test.html
可以看到,此时的控制台输出结果如下:
相信从这个打印输出,大家就可以很清晰地看到有多个拦截器和过滤器存在时的整个执行顺序了。当然,对于过个拦截器它们之间的执行顺序跟在SpringMVC的配置文件中定义的先后顺序有关
注:对于整个SpringMVC的执行流程来说,如果加上上面的拦截器和过滤器,其最终的执行流程就如下图所示:
大家还可以参考一下这个电子书的截图:
参考链接:spring过滤器和拦截器的区别和联系
参考链接:servlet容器,web容器,spring容器,springmvc容器的区别
参考链接:SpringMVC工作原理图