HandlerInterceptor 深度解析
一、核心方法
HandlerInterceptor 接口定义了三个核心方法,构成完整的请求生命周期拦截机制:
java
public interface HandlerInterceptor {
// 1. 请求预处理(Controller执行前)
default boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
return true; // 返回true继续流程,false中断请求
}
// 2. 请求后处理(Controller执行后,视图渲染前)
default void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {}
// 3. 请求完成回调(视图渲染后)
default void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {}
}
执行顺序 :多个拦截器时,preHandle()按配置顺序正序执行,而postHandle()和afterCompletion()按逆序执行。
二、主要作用
HandlerInterceptor的核心作用可归纳为:
1. 请求预处理
在Controller方法执行前进行通用操作,如权限验证、日志记录、参数修改等。通过返回布尔值决定是否继续执行后续流程。
2. 请求后处理
在Controller执行后、视图渲染前对ModelAndView对象进行操作,可修改模型数据或视图属性。
3. 资源清理
在整个请求完成后(包括视图渲染)执行资源释放操作,如关闭文件流、释放数据库连接,防止资源泄漏。
三、应用场景
高频场景
| 场景 | 实现方式 | 典型方法 |
|---|---|---|
| 登录鉴权 | 检查Session/JWT令牌 | preHandle() |
| 接口限流 | 基于Redis计数器 | preHandle() |
| 日志追踪 | 生成TraceId并写入MDC | preHandle() + afterCompletion() |
| 参数加解密 | 对敏感参数统一处理 | preHandle() |
| 性能监控 | 记录请求耗时 | preHandle() + afterCompletion() |
| 多租户隔离 | 解析租户ID并设置上下文 | preHandle() |
特殊场景
- 跨域处理:动态添加CORS响应头
- 请求头统一处理 :解析
Authorization、X-Request-ID等元数据 - 异常信息记录 :在
afterCompletion()中捕获并记录控制器抛出的异常
四、注意事项与最佳实践
⚠️ 关键注意事项
-
执行顺序控制
- 拦截器执行顺序由注册顺序决定,可使用
@Order注解调整优先级(值越小越先执行) - 外层拦截器的
preHandle()返回false会阻止内层拦截器执行
- 拦截器执行顺序由注册顺序决定,可使用
-
与Filter的区别
- HandlerInterceptor:运行于Spring MVC框架内,可访问Spring上下文,精准拦截Controller
- Filter:基于Servlet规范,更底层,可拦截所有请求(包括静态资源)
-
与AOP的选型
- HandlerInterceptor:适用于Web层全局请求处理
- AOP:适用于业务方法级别的横切关注点(如事务、缓存)
✅ 最佳实践
-
保持可测试性:将核心业务逻辑抽出到独立的Service或工具类,避免拦截器成为"不可测试的黑盒"
java// 推荐做法 if (!authService.checkPermission(user, request.getRequestURI())) { log.warn("无权限访问: {}", request.getRequestURI()); response.sendRedirect("/403"); return false; } -
避免臃肿逻辑:不要在拦截器中编写大量业务判断代码
-
增强可观测性:为拦截器添加日志和Trace ID,便于分布式链路追踪和问题排查
-
异常处理结合 :配合
@ControllerAdvice实现全局异常捕获与处理 -
资源及时释放 :必须在
afterCompletion()中释放线程本地变量(ThreadLocal),防止内存泄漏
五、典型配置示例
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/api/ **") // 拦截路径
.excludePathPatterns("/api/login", "/api/public/ **"); // 排除路径
}
}
通过合理运用HandlerInterceptor,可构建健壮的"请求防火墙",提升系统的安全性、可维护性与可扩展性。