[关闭]
@survivorZzz 2018-10-18T03:01:42.000000Z 字数 11619 阅读 1507

Spring MVC拦截器用以及和Servlet Filter的区别

SpringMVC Spring


说到拦截器, 首先要说一下Spring MVC的工作流程图(借用网络图):
249993-20161212142542042-2117679195.jpg-204.2kB

Spring MVC中的拦截器指的就是图中步骤三中HandlerExecutionChain处理器执行链中的HandlerInterceptor.
一个请求首先到达前端控制器DispatcherServlet后前端控制器将该请求指派给预先定义或默认的处理器映射器HandleMapping去根据请求路径找到具体的处理器(代码中指的是'Controller')来处理该请求, 处理器映射器'HandlerMapping'找到具体的处理器之后会返回处理器执行链, 执行链包括一系列的拦截器(如果有)和目标处理器'Controller', 再由前端控制器请求处理器适配器HandlerAdapter去请求执行执行链中的一个一个拦截器的preHandle()方法和目标处理器Controller的逻辑代码, 在处理器逻辑代码执行完, 前端控制器使用ModelAndView渲染视图之前执行一个一个拦截器的postHandle()方法以对处理器执行结果做二次加工操作.


定义自己的拦截器需要实现org.springframework.web.servlet.HandlerInterceptor接口或者继承该接口的适配器org.springframework.web.servlet.handler.HandlerInterceptorAdapter.

其中需要实现或重写三个关键方法:

关于以上三个方法的执行顺序, 觉知此事要躬行:
定义两个拦截器:

  1. 拦截器1:
  2. public class Interceptor1 implements HandlerInterceptor {
  3. @Override
  4. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
  5. System.err.println(">>>>>>>>>>>>> Interceptor1 preHandle() ");
  6. return true;
  7. }
  8. @Override
  9. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
  10. System.err.println(">>>>>>>>>>>>> Interceptor1 postHandle() ");
  11. }
  12. @Override
  13. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
  14. @Nullable Exception ex) throws Exception {
  15. System.err.println(">>>>>>>>>>>>> Interceptor1 afterCompletion() ");
  16. }
  17. }
  1. 拦截器2:
  2. public class Interceptor2 implements HandlerInterceptor {
  3. @Override
  4. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
  5. throws Exception {
  6. System.err.println(">>>>>>>>>>>>> Interceptor2 preHandle() ");
  7. return true;
  8. }
  9. @Override
  10. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
  11. @Nullable ModelAndView modelAndView) throws Exception {
  12. System.err.println(">>>>>>>>>>>>> Interceptor2 postHandle() ");
  13. }
  14. @Override
  15. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
  16. @Nullable Exception ex) throws Exception {
  17. System.err.println(">>>>>>>>>>>>> Interceptor2 afterCompletion() ");
  18. }
  19. }

定义一个处理器Controller:

  1. @Controller
  2. public class MyController {
  3. @ResponseBody
  4. @RequestMapping("/my/haha")
  5. public String method() {
  6. System.out.println(">>>>>>>> MyController#method running ");
  7. return "haha";
  8. }
  9. }

用java config的方式配置这两个拦截器:

  1. @Configuration
  2. @EnableWebMvc
  3. public class MyWebMvcConfig implements WebMvcConfigurer {
  4. @Override
  5. public void addInterceptors(InterceptorRegistry registry) {
  6. registry.addInterceptor(new Interceptor1()).addPathPatterns("/my/**");
  7. registry.addInterceptor(new Interceptor2()).addPathPatterns("/my/**");
  8. }
  9. }

请求:http://localhost:9898/my/haha

控制台打印(亲测!!!):

  1. >>>>>>>>>>>>> Interceptor1 preHandle()
  2. >>>>>>>>>>>>> Interceptor2 preHandle()
  3. >>>>>>>> MyController#method running
  4. >>>>>>>>>>>>> Interceptor2 postHandle()
  5. >>>>>>>>>>>>> Interceptor1 postHandle()
  6. >>>>>>>>>>>>> Interceptor2 afterCompletion()
  7. >>>>>>>>>>>>> Interceptor1 afterCompletion()

注意拦截器的添加顺序!

如果我先添加的是第二个拦截器, 然后再添加第一个拦截器, 则输出为:

  1. >>>>>>>>>>>>> Interceptor2 preHandle()
  2. >>>>>>>>>>>>> Interceptor1 preHandle()
  3. >>>>>>>> MyController#method running
  4. >>>>>>>>>>>>> Interceptor1 postHandle()
  5. >>>>>>>>>>>>> Interceptor2 postHandle()
  6. >>>>>>>>>>>>> Interceptor1 afterCompletion()
  7. >>>>>>>>>>>>> Interceptor2 afterCompletion()

结论, 执行顺序 :
preHandle()(拦截器添加顺序正序) -> 目标处理器COntroller -> postHandle()(拦截器添加顺序反序) -> afterCompletion()(拦截器添加顺序反序)

额外的, 除了上述代码中通过java config方式配置拦截器, 也可通过xml文件的方式配置,例如:
springmvc.xml:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:mvc="http://www.springframework.org/schema/mvc"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
  8. http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
  9. <mvc:interceptors>
  10. <!--注意拦截器的配置顺序, 例如最后一个全局拦截器WholeScopeInterceptor
  11. 的preHandle方法会在最后执行-->
  12. <mvc:interceptor>
  13. <mvc:mapping path="/my/**"/>
  14. <!--<mvc:exclude-mapping path="排除的路径"/>-->
  15. <bean class="com.example.demo.Interceptors.Interceptor1"/>
  16. </mvc:interceptor>
  17. <mvc:interceptor>
  18. <mvc:mapping path="/my/**"/>
  19. <bean class="com.example.demo.Interceptors.Interceptor2"/>
  20. </mvc:interceptor>
  21. <mvc:interceptor>
  22. <mvc:mapping path="/your/**"/>
  23. <bean class="com.example.demo.Interceptors.Interceptor3"/>
  24. </mvc:interceptor>
  25. <!--直接定义在<mvc:interceptors>一级的拦截器会拦截所有的请求-->
  26. <bean class="com.example.demo.Interceptors.WholeScopeInterceptor"/>
  27. </mvc:interceptors>
  28. </beans>

来测试一下:

  1. @Controller
  2. public class YourController {
  3. @ResponseBody
  4. @RequestMapping("/your/haha")
  5. public String method() {
  6. System.out.println(">>>>>>>> YourController#method running ");
  7. return "haha";
  8. }
  9. }
  1. public class Interceptor3 implements HandlerInterceptor {
  2. @Override
  3. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
  4. throws Exception {
  5. System.err.println(">>>>>>>>>>>>> Interceptor3 preHandle() ");
  6. return true;
  7. }
  8. @Override
  9. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
  10. @Nullable ModelAndView modelAndView) throws Exception {
  11. System.err.println(">>>>>>>>>>>>> Interceptor3 postHandle() ");
  12. }
  13. @Override
  14. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
  15. @Nullable Exception ex) throws Exception {
  16. System.err.println(">>>>>>>>>>>>> Interceptor3 afterCompletion() ");
  17. }
  18. }
  1. public class WholeScopeInterceptor implements HandlerInterceptor {
  2. @Override
  3. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
  4. throws Exception {
  5. System.err.println(">>>>>>>>>>>>> WholeScopeInterceptor preHandle() ");
  6. return true;
  7. }
  8. @Override
  9. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
  10. @Nullable ModelAndView modelAndView) throws Exception {
  11. System.err.println(">>>>>>>>>>>>> WholeScopeInterceptor postHandle() ");
  12. }
  13. @Override
  14. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
  15. @Nullable Exception ex) throws Exception {
  16. System.err.println(">>>>>>>>>>>>> WholeScopeInterceptor afterCompletion() ");
  17. }
  18. }

请求http://localhost:9898/your/haha控制台输出:

  1. >>>>>>>>>>>>> Interceptor3 preHandle()
  2. >>>>>>>>>>>>> WholeScopeInterceptor preHandle()
  3. >>>>>>>> YourController#method running
  4. >>>>>>>>>>>>> WholeScopeInterceptor postHandle()
  5. >>>>>>>>>>>>> Interceptor3 postHandle()
  6. >>>>>>>>>>>>> WholeScopeInterceptor afterCompletion()
  7. >>>>>>>>>>>>> Interceptor3 afterCompletion()

请求http://localhost:9898/my/haha控制台输出:

  1. >>>>>>>>>>>>> Interceptor1 preHandle()
  2. >>>>>>>>>>>>> Interceptor2 preHandle()
  3. >>>>>>>>>>>>> WholeScopeInterceptor preHandle()
  4. >>>>>>>> MyController#method running
  5. >>>>>>>>>>>>> WholeScopeInterceptor postHandle()
  6. >>>>>>>>>>>>> Interceptor2 postHandle()
  7. >>>>>>>>>>>>> Interceptor1 postHandle()
  8. >>>>>>>>>>>>> WholeScopeInterceptor afterCompletion()
  9. >>>>>>>>>>>>> Interceptor2 afterCompletion()
  10. >>>>>>>>>>>>> Interceptor1 afterCompletion()

额外的, 由于pringboot强调0配置文件方式, 推荐使用java config的方式. 如果非要使用xml配置文件的方式, 可以在使用@Configuration的地方使用@ImportResource("springmvc.xml")导入该配置文件, 由于@SpringBootApplication注解被@Configuration所标注,所以在@SpringBootApplication处导入也是一样的:

  1. @SpringBootApplication
  2. @ImportResource("springmvc.xml")
  3. public class DemoApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(DemoApplication.class, args);
  6. }
  7. // @Bean
  8. // public MyClass newMyClass() {
  9. // return new MyClass();
  10. // }
  11. }

说完SpringMvc的拦截器接下来说说servletFilter过滤器:
这里的Filter通常指的是javax.servlet.Filter, 该接口定义如下:

  1. public interface Filter {
  2. void init(FilterConfig var1) throws ServletException;
  3. void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
  4. void destroy();
  5. }

Filter可以拦截所有到达java web server的HTTP请求(如果web.xml配置文件中将该Filter的url-pattern配置成<url-pattern>/*</url-pattern>), 包括到达指定servlet的请求, 静态资源例如xxx.html的请求等.
servlet-filters-1.png-18.7kB
要定义自己的Filter需要实现javax.servlet.Filter接口:

  1. import javax.servlet.*;
  2. import java.io.IOException;
  3. /**
  4. */
  5. public class SimpleServletFilter implements Filter {
  6. public void init(FilterConfig filterConfig) throws ServletException {
  7. }
  8. public void doFilter(ServletRequest request, ServletResponse response,
  9. FilterChain filterChain)
  10. throws IOException, ServletException {
  11. }
  12. public void destroy() {
  13. }
  14. }

其中init()方法仅会在应用启动时执行一次, destroy()方法会在应用关闭时执行一次. 其中最重要的方法是doFilter()方法, 可在这个方法中对指定<url-pattern>xxx</url-pattern>的的所有请求的request对象进行过滤或阻塞.例如:

  1. public void doFilter(ServletRequest request, ServletResponse response,
  2. FilterChain filterChain)
  3. throws IOException, ServletException {
  4. String myParam = request.getParameter("myParam");
  5. if(!"blockTheRequest".equals(myParam)){
  6. filterChain.doFilter(request, response);
  7. }
  8. }

当请求达到这个过滤器的doFilter()方法, 判断request中是否有"blockTheRequest",如果有,则拦截该请求, 如果没有则通过filterChain.doFilter(request, response); 对该请求放行,让该请求到达下一个过滤器或者到达目标资源(如果这是最后一个filter).
配置自定义的拦截器需要在web.xml配置文件中配置, 例如:

  1. <filter>
  2. <filter-name>myFilter</filter-name>
  3. <filter-class>servlets.SimpleServletFilter</filter-class>
  4. </filter>
  5. <filter-mapping>
  6. <filter-name>myFilter</filter-name>
  7. <url-pattern>/*</url-pattern>
  8. </filter-mapping>

特别注意过滤器的配置顺序,过滤器的拦截顺序是以该文件中过滤器的配置顺序从上往下对请求进行过滤.


servlet Filter和SpringMVC拦截器的区别:

首先应用一段HandlerInterceptor接口中的doc注释

HandlerInterceptor is basically similar to a Servlet Filter, but in contrast to the latter it just allows custom pre-processing with the option of prohibiting the execution of the handler itself, and custom post-processing. Filters are more powerful, for example they allow for exchanging the request and response objects that are handed down the chain. Note that a filter gets configured in web.xml, a HandlerInterceptor in the application context.
As a basic guideline, fine-grained handler-related preprocessing tasks are candidates for HandlerInterceptor implementations, especially factored-out common handler code and authorization checks. On the other hand, a Filter is well-suited for request content and view content handling, like multipart forms and GZIP compression. This typically shows when one needs to map the filter to certain content types (e.g. images), or to all requests.


from:stack overflow 关于filter和SpringMVC拦截器的区别

postHandle will be called after handler method invocation but before the view being rendered. So, you can add more model objects to the view but you can not change the HttpServletResponse, since it has already committed. doFilter is much more versatile than the postHandle. You can change the request or response and pass it to the chain or even block the request processing.

HandlerInterceptorpostHandle()方法会在目标处理器Controller的目标方法被调用之后, 视图被渲染之前执行, 可以往执行结果ModelAndView中添加数据, 但是不能改变已经生成的HttpServletResponse对象 因为已经提交. 而Filter总的doFilter()方法则更强, 可以对HttpServletRequestHttpServletResponse进行更改, 甚至是阻塞这个请求.


那什么时候用Filter什么时候用SpringMVC的拦截器呢?
from:stack overflow 关于filter和SpringMVC拦截器的区别

As the doc said, fine-grained handler-related preprocessing tasks are candidates for HandlerInterceptor implementations, especially factored-out common handler code and authorization checks. On the other hand, a Filter is well-suited for request content and view content handling, like multipart forms and GZIP compression. This typically shows when one needs to map the filter to certain content types (e.g. images), or to all requests.

如果是在基于Spring的项目中, 用Filter能做到的事情, HandelerInterceptor也能做到, 并且能做到更多(拦截器是Spring容器中的bean, 可以注入其他bean), 更优雅.
更多解释看这里1, 看这里2


额外的, Spring还有另一个拦截器接口org.springframework.web.context.request.WebRequestInterceptor
该接口的定义:

  1. public interface WebRequestInterceptor {
  2. void preHandle(WebRequest request) throws Exception;
  3. void postHandle(WebRequest request, ModelMap model) throws Exception;
  4. void afterCompletion(WebRequest request, Exception ex) throws Exception;
  5. }

你可能乍眼一看发现和HandlerInterceptor的接口定义好像, 但是看方法签名, 会发现里面只有request对象的封装对象, 而没有response对象, 就是说你没办法通过这个接口的方法改变response但是刻意对request做一些操作, 比如可以往request域对象中添加Hibernate SqlSession等.
该接口的preHandle()doc如是说:

Intercept the execution of a request handler before its invocation.
Allows for preparing context resources (such as a Hibernate Session) and expose them as request attributes or as thread-local objects


如果需要对处理器目标方法的执行结果做一些操作, 必须使用HandlerInterceptorWebRequestInterceptor做不到!

更多关于HandlerInterceptorWebRequestInterceptor区别 看这里


不正之处, 欢迎大佬指正, 相互学习!

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注