type
status
date
slug
summary
tags
category
icon
password

一、Interceptor 介绍

Interceptor 是 Spring MVC 提供的拦截器机制,类似于 Servlet 规范中的过滤器 Filter,作用是拦截控制器的执行并添加自定义操作,一般用来做一些通用的功能,如请求日志打印、权限控制等。

Interceptor 和 Filter 的区别

  • 规范不同:Filter 是 Servlet 规范规定的,被 Tomcat 调用;Interceptor 是 Spring 框架提供的,被 Spring 调用。
  • 是否受 Spring 管理:Filter 不能够使用 Spring 容器资源,Interceptor 作为 Spring 容器的组件,可以使用 Spring 容器的任何资源。
  • 作用范围不同:Filter 只在 Servlet 的前后起作用,Filter 组件实际上并不知道后续内部处理是 Spring MVC 提供的 DispatcherServlet 还是其他 Servlet 组件,它可以应用在所有 Servlet 上;而 Interceptor 仅仅针对 DispatcherServlet 的 Controller 前后进行拦截,相比 Filter 更深入方法内部。
notion image
面试常问题目:如果 Listener、Filter、 Interceptor、 AOP都存在,它们的执行顺序如何? Listener => Filter => Interceptor => AOP

Interceptor 接口

在 Spring MVC 中定义一个 Interceptor 有两种方法:
  • 实现 HandlerInterceptor 接口。
  • 实现 WebRequestInterceptor 接口。
HandlerInterceptorWebRequestInterceptor 的异同:
相同点:
  • 两个接口都可用于 Contrller 层请求拦截,接口中定义的方法作用位置也是一样的。
  • WebRequestInterceptor 通过 WebRequestHandlerInterceptorAdapter 间接实现了 HandlerInterceptor
不同点:
  • 入参不同:HandlerInterceptor 的入参是 Servlet 原生的 HttpServletRequest 和 HttpServletResponse;WebRequestInterceptor的入参只有 WebRequest,里面对 HttpServletRequest 进行了一层包装,方便获取 Request 内容(这个接口的实现就是为了方便获取 Request 中的信息,或者预设一些参数供后续流程使用)。
  • preHandle 返回值不同:WebRequestInterceptorpreHandle 是没有返回值的,说明该方法中的逻辑并不影响后续的方法执行;HandlerInterceptor 可以在 preHandle 方法返回 false,拒绝请求进入 controller 方法。
我们一般主要使用 HandlerInterceptor 接口,HandlerInterceptor 接口的定义如下:
这个接口里面包含三个方法:
  • preHandle:处理器执行之前执行,如果返回 false 将跳过处理器、拦截器 postHandle 方法、视图渲染等,直接执行拦截器 afterCompletion 方法。
  • postHandle:处理器执行后,视图渲染前执行,如果处理器抛出异常,将跳过该方法直接执行拦截器 afterCompletion 方法。
  • afterCompletion:视图渲染后执行,不管处理器是否抛出异常,该方法都将执行。
上面这个过程使用流程图表示如下:
notion image
注意:自从前后端分离之后,Spring MVC 中的处理器方法执行后通常不会再返回视图,而是返回表示 json 或 xml 的对象,@Controller 方法返回值类型如果为 ResponseEntity 或标注了 @ResponseBody 注解,此时处理器方法一旦执行结束,Spring 将使用 HandlerMethodReturnValueHandler 对返回值进行处理,具体来说会将返回值转换为 json 或 xml,然后写入响应,后续也不会进行视图渲染,这时 postHandle 将没有机会修改响应体内容。
如果需要更改响应内容,可以定义一个实现 ResponseBodyAdvice 接口的类,然后将这个类直接定义到 RequestMappingHandlerAdapter 中的 requestResponseBodyAdvice 或通过 @ControllerAdvice 注解添加到 RequestMappingHandlerAdapter。

二、Interceptor 使用

要实现一个自定义的 Interceptor,以 HandlerInterceptor 接口为例,直接实现 HandlerInterceptor 接口即可。
为了避免实现该接口的所有方法,Spring 5 之前提供了一个抽象的实现 HandlerInterceptorAdapter,Java 8 接口 default 方法新特性出现后,我们直接实现 HandlerInterceptor 接口即可。
要把实现的 Interceptor 添加到 Spring 容器发挥作用,有四种配置方式:
  • XML 配置
  • @Bean 注解配置
  • 实现 WebMvcConfigurer 接口(推荐使用)
  • 继承 WebMvcConfigurationSupport (不推荐使用)

XML 配置

  • bean:mvc:interceptors 标签下的拦截器 bean 将应用到所有的处理器。
  • mvc:interceptor:这个标签下的子标签可以指定拦截器应用到哪些请求路径。
    • mvc:mapping:指定处理的请求路径。
    • mvc:exclude-mapping:指定排除的请求路径。
    • bean:指定应用到给定路径的拦截器 bean。

@Bean 注解配置

实现 WebMvcConfigurer 接口(推荐使用)

这里在配置类上添加了 @EnableWebMvc 注解开启了 Spring MVC 中的某些特性,然后就可以实现 WebMvcConfigurer 接口中的 addInterceptors 方法向 Spring MVC 中添加拦截器。如果你使用了 spring-boot-starter-web,不再需要手工添加 @EnableWebMvc 注解。
旧版本的 Spring 的写法是继承 WebMvcConfigurerAdapter 来重写 addCorsMappings。Spring 5.x 以后已经抛弃了 WebMvcConfigurerAdapter ,理由同 HandlerInterceptorAdapter,所以尽量不要再使用 WebMvcConfigurerAdapter

继承 WebMvcConfigurationSupport (不推荐使用)

用法和 WebMvcConfigurer 接口类似,但是不推荐这种写法。原因是 Spring Boot 限制,只有当 WebMvcConfigurationSupport 类不存在的时候 WebMvcAutoConfiguration 类才会生效。WebMvcAutoConfiguration 类中不仅定义了 classpath:/META-INF/resources/classpath:/resources/classpath:/static/classpath:/public/ 等路径的映射,还定义了配置文件 spring.mvc 开头的配置信息等。当 WebMvcAutoConfiguration 不生效时会导致以下几个问题:
  • WebMvcProperties 和 ResourceProperties 失效
  • 类路径上的 HttpMessageConverter 失效

定义多个 Interceptor

如果定义了多个 Interceptor,多个 Interceptor 的执行顺序如下。
  • preHandle 按照 Interceptor 的顺序先后执行。如果任意一次调用返回 false 则直接跳到最后一个 Interceptor 的 afterCompletion 开始逆序执行。
  • postHandle 按照 Interceptor 的逆序先后执行,也就说后面的 Interceptor 先执行 postHandle。
  • afterCompletion 也按照 Interceptor 的逆序先后执行,后面的拦截器先执行 afterCompletion。
上面执行顺序使用流程图表示如下:
notion image
那么多个 Interceptor 的顺序是如何指定的呢?
  • 如果使用 xml 配置,Spring 将记录 bean 声明的顺序,先声明的拦截器将排在前面。
  • 如果使用 @Bean 注解配置,由于通过反射读取方法无法保证顺序,因此需要在方法上添加 @Order 注解指定 bean 的声明顺序。@Order 数值越小,优先级越高,排在队列更前面。
  • 如果实现 WebMvcConfigurer 接口,拦截器的顺序并非和添加顺序完全保持一致,为了控制先后顺序,需要自定义的拦截器实现 Ordered 接口。Ordered 使用同 @Order 注解。
 

三、Interceptor 配置

Pattern 配置

简单来说:
  • 一个 *:只匹配字符,不匹配路径 /
  • 两个 **:匹配字符,和路径 /
假如我们需要拦截以下 swagger 请求之外的其他所有请求。
我们的拦截器表达式可以这样写

参考

Spring MVC源码:DispatcherServletSpring MVC:解决跨域问题
mcbilla
mcbilla
一个普通的干饭人🍚
Announcement
type
status
date
slug
summary
tags
category
icon
password
🎉欢迎来到飙戈的博客🎉
-- 感谢您的支持 ---
👏欢迎学习交流👏