💡 前言
在 Web 开发中,我们经常需要对请求进行统一处理,比如:
- 判断用户是否已登录
- 记录请求日志
- 实现接口权限校验
- 防止重复提交
- 统一设置响应头信息
这些功能如果都写在每个 Controller 里,不仅代码冗余,还难以维护。而 Spring Boot 提供了强大的拦截器(Interceptor)机制,让我们可以像 AOP 一样,在请求到达 Controller 之前或之后进行统一处理。
本文将带你从零开始掌握 Spring Boot 拦截器的使用,并通过多个实用案例展示其强大之处!
🧪 一、什么是 Spring Boot 拦截器?
✅ 定义:
拦截器(HandlerInterceptor
)是 Spring MVC 提供的一种用于拦截 HTTP 请求的机制。它可以在请求被 Controller 处理前后执行特定逻辑。
✅ 与 Filter 的区别:
对比项 | Interceptor(拦截器) | Filter(过滤器) |
---|---|---|
所属框架 | Spring MVC | Servlet 标准 |
精确度 | 可以精确到 Controller 方法级别 | 作用于所有请求 |
支持 Spring 注入 | ✅ 支持 | ❌ 不支持 |
执行时机 | preHandle -> Controller -> postHandle -> afterCompletion | doFilter |
📌 适用场景:
- 登录状态校验
- 接口权限控制
- 请求日志记录
- 统一返回格式封装
- 防止重复提交
🛠️ 二、自定义拦截器实现步骤
Step 1:创建拦截器类
实现 HandlerInterceptor
接口,并重写以下三个方法:
java
public class LoginInterceptor implements HandlerInterceptor {
// 请求进入 Controller 之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (token == null || !token.equals("abc123")) {
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\":401,\"message\":\"未登录\"}");
return false; // 中断请求
}
return true;
}
// Controller 方法执行完成后执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 可选操作,如添加响应头
response.setHeader("X-Custom-Header", "interceptor-added");
}
// 整个请求完成(包括视图渲染)后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 可用于资源清理、日志记录等
System.out.println("【拦截器】请求结束:" + request.getRequestURI());
}
}
Step 2:注册拦截器
创建配置类,继承 WebMvcConfigurer
并重写 addInterceptors()
方法:
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") // 拦截所有路径
.excludePathPatterns("/login", "/error"); // 排除不需要拦截的路径
}
}
🎯 三、拦截器典型应用场景实战
✅ 场景一:登录鉴权(Token 校验)
拦截所有非登录请求,判断请求头中是否携带有效 Token。
java
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (token == null || !isValidToken(token)) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
}
return true;
}
✅ 场景二:请求日志记录
记录每次请求的 URL、IP、耗时、参数等信息。
java
private static final ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
startTimeThreadLocal.set(System.currentTimeMillis());
System.out.println("【请求开始】IP: " + request.getRemoteAddr() + ", URI: " + request.getRequestURI());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
long duration = System.currentTimeMillis() - startTimeThreadLocal.get();
System.out.println("【请求结束】耗时: " + duration + "ms");
startTimeThreadLocal.remove();
}
✅ 场景三:接口权限控制(基于角色)
根据当前用户的角色判断是否有权限访问某个接口。
java
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
User user = getCurrentUser(request);
if (!user.hasPermission(request.getRequestURI())) {
response.getWriter().write("{\"code\":403,\"message\":\"无权限\"}");
return false;
}
return true;
}
✅ 场景四:防止重复提交
通过 Token 或 Redis 缓存来识别重复请求。
java
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getParameter("formToken");
if (StringUtils.isEmpty(token) || RedisUtil.exists(token)) {
response.getWriter().write("{\"code\":400,\"message\":\"请勿重复提交\"}");
return false;
}
RedisUtil.set(token, "submitted", 60); // 设置有效期
return true;
}
🧭 四、拦截器使用注意事项
注意事项 | 说明 |
---|---|
拦截器顺序问题 | 多个拦截器按照注册顺序依次执行 preHandle ,逆序执行 afterCompletion |
不要阻塞主线程 | 如需异步处理,请使用线程池 |
避免频繁调用耗时操作 | 如数据库查询、远程调用等,可能影响性能 |
使用 ThreadLocal 要注意内存泄漏 | 每次请求结束后记得 remove 清理 |
不建议替代 Filter | 如跨域、编码处理等仍推荐使用 Filter |
📘 五、总结对比表
功能 | 说明 |
---|---|
preHandle | 在 Controller 执行前调用,可中断请求 |
postHandle | 在 Controller 执行后、视图渲染前调用 |
afterCompletion | 请求完全结束后调用,可用于资源释放 |
应用场景 | 登录校验、权限控制、日志记录、防重提交 |
优点 | 解耦业务逻辑、统一处理请求 |
缺点 | 滥用会影响性能、增加系统复杂度 |
🎁 六、结语
Spring Boot 拦截器是构建高质量 Web 应用不可或缺的利器之一。通过合理使用拦截器,我们可以优雅地实现登录验证、权限控制、日志记录等通用功能,使代码更简洁、系统更健壮。
无论你是开发企业级后台管理系统,还是搭建高并发 API 接口平台,都应该掌握拦截器的使用技巧。
🎯 点赞、收藏、转发本文,让更多开发者受益!