Spring MVC 拦截器interceptor
Spring MVC 过滤器interceptor
- 拦截器
- 定义
- 概念与工作原理
- **执行顺序**
- 多拦截器执行顺序
- 优势
- 拦截器实现代码
- xml 配置方式
- 1. 实现HandlerInterceptor接口
- 2. 配置XML文件,声明拦截路径
- 3. 访问路径,触发拦截器
- 配置类方式
- 1. 实现HandlerInterceptor接口。
- 2. 配置类实现webMvcConfigurer接口重载addInterceptors方法。
- 3. 测试拦截器效果。
- 拦截器 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接口
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;public class UserInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle:handler触发之前");return true;}@Overridepublic void postHandle(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle:handler触发之后");}@Overridepublic 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 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. 访问路径,触发拦截器
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,链接返回结果:
preHandle
postHandle
afterCompletion
- 访问/user/exception,链接返回结果:
- 页面报错500,因此postHandler不会触发,但是afterCompletion无论是否出现异常,都会执行。
preHandle
afterCompletion
配置类方式
1. 实现HandlerInterceptor接口。
- 通过
@Component
注解,添加到容器中。
@Component
public class UserInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle");return HandlerInterceptor.super.preHandle(request, response, handler);}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion");}
}
2. 配置类实现webMvcConfigurer接口重载addInterceptors方法。
- 一定不能忘记添加
@Configuration
注解。 - 实现接口
WebMvcConfigurer
接口,并重载addInterceptors
方法。
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {@Autowiredprivate UserInterceptor userInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(userInterceptor).addPathPatterns("/user/**");}
}
3. 测试拦截器效果。
@RestController
@RequestMapping("user")
public class UserController {@GetMapping("info")public String info(){return "user info"+ new Date();}
}
- 结果输出:
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 之前。 |