Spring MVC 过滤器interceptor
- 拦截器
- 拦截器实现代码
-
- [xml 配置方式](#xml 配置方式)
-
- [1. 实现HandlerInterceptor接口](#1. 实现HandlerInterceptor接口)
- [2. 配置XML文件,声明拦截路径](#2. 配置XML文件,声明拦截路径)
- [3. 访问路径,触发拦截器](#3. 访问路径,触发拦截器)
- 配置类方式
-
- [1. 实现HandlerInterceptor接口。](#1. 实现HandlerInterceptor接口。)
- [2. 配置类实现webMvcConfigurer接口重载addInterceptors方法。](#2. 配置类实现webMvcConfigurer接口重载addInterceptors方法。)
- [3. 测试拦截器效果。](#3. 测试拦截器效果。)
- [拦截器 vs. 过滤器](#拦截器 vs. 过滤器)
示例代码地址:https://gitee.com/hua5h6m/framework-java/tree/master/spring-mvc-interceptor

拦截器
定义
Spring 拦截器 是 Spring MVC 框架提供的一种强大机制,它允许您在 HTTP 请求进入控制器 或 控制器处理完 请求后以及最终返回给用户前,注入自定义的预处理和后处理逻辑。
概念与工作原理
拦截器的工作流程基于 AOP 的设计思想,它围绕着 控制器方法的执行 进行拦截。
一个完整的拦截器生命周期包含三个关键时机点:
-
preHandle- 预处理 :决定是否拦截,返回值为Boolean类型。- 时机 :在请求被 DispatcherServlet 接收后,但在调用目标 Controller(
Handler)方法 之前 执行。 - 作用 :这是最常用、最重要的方法。常用于:
- 权限验证:检查用户是否登录,是否有权限访问该接口。
- 日志记录:记录请求信息,如 URL、IP、参数等。
- 性能监控:记录请求开始时间。
- 参数预处理:对请求参数进行统一的格式化或校验。
- 返回值 :
boolean类型。true:继续执行流程,调用下一个拦截器或最终的 Controller 方法。false:中断请求,不再继续向下执行。通常在此方法中直接通过response返回结果。
- 时机 :在请求被 DispatcherServlet 接收后,但在调用目标 Controller(
-
postHandle- 后处理- 时机 :在 Controller 方法执行 之后 ,但在视图渲染或返回 JSON 之前 执行。
- 作用 :
- 对 Controller 返回的
ModelAndView对象进行修改(例如,向模型中添加一些全局数据)。 - 在返回视图前进行一些处理。
- 对 Controller 返回的
- 注意 :如果 Controller 方法内部发生了异常,此方法将 不会 被调用。
-
afterCompletion- 请求完成回调- 时机 :在整个请求完成 之后 ,即视图渲染完毕或响应已返回给客户端 之后 执行。
- 作用 :
- 资源清理 :如清理
preHandle中创建的线程局部变量。 - 性能监控收尾 :计算整个请求的耗时(需要与
preHandle中记录的开始时间配合)。 - 记录最终日志。
- 资源清理 :如清理
- 注意 :无论 Controller 方法执行是否成功,此方法 总会被调用 (类似于
try-catch-finally中的finally块)。
执行顺序
Filter.doFilter() -> DispatcherServlet -> Interceptor.preHandle() -> Controller -> Interceptor.postHandle() -> 渲染视图 -> Interceptor.afterCompletion()
多拦截器执行顺序
在Spring MVC中,当存在多个拦截器时,它们的执行顺序由配置的先后顺序决定 。具体来说,在配置类中通过InterceptorRegistry添加拦截器的顺序就是拦截器的执行顺序。或者在XML中,谁先配置谁先执行。
假设我们有两个拦截器:InterceptorA和InterceptorB,并且我们先添加InterceptorA,再添加InterceptorB。
- 正常情况下的执行顺序(所有拦截器的
preHandle都返回true):
InterceptorA.preHandle()InterceptorB.preHandle()- 执行控制器方法(Handler)
InterceptorB.postHandle()InterceptorA.postHandle()InterceptorB.afterCompletion()InterceptorA.afterCompletion()
注意:preHandle方法是按照配置 顺序执行的,而postHandle和afterCompletion则是按照配置顺序的反向 顺序执行。先进后出原则。
优势
- 核心概念:Spring 拦截器是 Spring MVC 中用于在控制器方法执行前后插入逻辑的组件。
- 生命周期 :包含
preHandle(最常用)、postHandle和afterCompletion三个关键方法。 - 主要应用 :权限校验、日志记录、性能监控、通用参数处理 等横切关注点。
- 核心优势:将通用的、非业务性的代码从业务控制器中解耦出来,使代码更加清晰、可维护和可复用。
拦截器实现代码
xml 配置方式
1. 实现HandlerInterceptor接口
java
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class UserInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle:handler触发之前");
return true;
}
@Override
public void postHandle(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle:handler触发之后");
}
@Override
public void afterCompletion(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion:");
}
}
2. 配置XML文件,声明拦截路径
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--配置注解扫码路径-->
<context:component-scan base-package="com.maven" />
<!--配置控制器映射器和控制器适配器-->
<mvc:annotation-driven />
<!--配置静态资源解析-->
<mvc:default-servlet-handler />
<!--配置拦截器-->
<mvc:interceptors>
<!--第一个拦截器-->
<mvc:interceptor>
<!--拦截那些路径:默认全部拦截-->
<mvc:mapping path="/user/**"/>
<!--配置不拦截的路径-->
<mvc:exclude-mapping path="/user/list"/>
<!--指定拦截器的位置-->
<bean class="com.maven.interceptor.UserInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
</beans>
3. 访问路径,触发拦截器
java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("user")
public class UserController {
@GetMapping("info")
public String info() {
return "user info";
}
@GetMapping("list")
public String list() {
return "user list";
}
@GetMapping("exception")
public String exception() {
int i = 1 / 0;
return "user exception";
}
}
- 访问/user/info,链接返回结果:
java
preHandle
postHandle
afterCompletion
- 访问/user/exception,链接返回结果:
- 页面报错500,因此postHandler不会触发,但是afterCompletion无论是否出现异常,都会执行。
java
preHandle
afterCompletion
配置类方式
1. 实现HandlerInterceptor接口。
- 通过
@Component注解,添加到容器中。
java
@Component
public class UserInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
2. 配置类实现webMvcConfigurer接口重载addInterceptors方法。
- 一定不能忘记添加
@Configuration注解。 - 实现接口
WebMvcConfigurer接口,并重载addInterceptors方法。
java
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Autowired
private UserInterceptor userInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(userInterceptor).addPathPatterns("/user/**");
}
}
3. 测试拦截器效果。
java
@RestController
@RequestMapping("user")
public class UserController {
@GetMapping("info")
public String info(){
return "user info"+ new Date();
}
}
- 结果输出:
java
preHandle
postHandle
afterCompletion
拦截器 vs. 过滤器
这是一个非常常见的面试题,理解它们的区别至关重要。
| 特性 | Spring 拦截器 | Servlet 过滤器 |
|---|---|---|
| 出身 | Spring 框架 特有的组件。 | Servlet 规范 的一部分,任何 Java Web 应用都能用。 |
| 依赖 | 依赖于 Spring 容器。 | 不依赖于 Spring,只依赖于 Servlet 容器(如 Tomcat)。 |
| 作用域 | 只能拦截 Spring MVC 的请求(即 DispatcherServlet 处理的请求)。 | 可以拦截 所有 进入容器的请求(包括静态资源、JSP等)。 |
| 获取Bean | 可以轻松获取和注入 Spring 容器中的其他 Bean(如 Service)。 | 默认不能,需要通过 SpringBeanAutowiringSupport 等特殊方式。 |
| 控制粒度 | 可以获取到具体的 Handler(控制器方法) 信息。 | 只能获取到 ServletRequest 和 ServletResponse。 |
| 执行时机 | 在 DispatcherServlet 内部,Controller 方法执行的前后。 | 在 DispatcherServlet 之前。 |