Spring Boot 拦截器使用全指南
- 发布时间:2026-03-10 03:28:09
- 本文热度:浏览 9 赞 0 评论 0
- 文章标签: Spring Boot Java Web 后端开发
- 全文共1字,阅读约需1分钟
一、拦截器的基本概念
在 Web 应用开发中,经常需要在请求处理前后执行一些通用逻辑,例如:
- 登录鉴权
- 请求日志记录
- 接口调用统计
- 参数校验
- 统一响应处理
- 接口限流
如果在每一个 Controller 方法中编写这些逻辑,不仅会造成代码重复,还会破坏业务代码的整洁性。
Spring Boot 基于 Spring MVC,提供了 HandlerInterceptor(拦截器)机制,用于在 请求进入 Controller 之前或响应返回之后执行统一处理逻辑。
拦截器的执行流程如下:
客户端请求
│
▼
DispatcherServlet
│
▼
HandlerMapping
│
▼
HandlerInterceptor.preHandle()
│
▼
Controller 方法执行
│
▼
HandlerInterceptor.postHandle()
│
▼
视图渲染
│
▼
HandlerInterceptor.afterCompletion()
│
▼
响应返回客户端
拦截器属于 Spring MVC 体系,其作用范围仅限于 Controller 层请求处理流程。
二、拦截器的核心接口 HandlerInterceptor
Spring MVC 拦截器核心接口为:
org.springframework.web.servlet.HandlerInterceptor
主要包含三个方法:
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception;
void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception;
void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception;
}
1 preHandle
请求进入 Controller 之前执行。
返回值决定是否继续执行后续流程:
true:继续执行 Controllerfalse:中断请求
示例:
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("请求进入拦截器");
return true;
}
典型使用场景:
- 登录校验
- token 校验
- 接口签名验证
- 参数检查
- 防重复提交
2 postHandle
Controller 方法执行完成后,但 视图渲染之前执行。
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("Controller 执行完成");
}
常见用途:
- 修改返回数据
- 添加公共模型数据
- 统一视图处理
如果接口返回 JSON(如 REST API),通常较少使用此方法。
3 afterCompletion
请求 完全结束后执行。
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
System.out.println("请求完成");
}
典型用途:
- 请求耗时统计
- 资源释放
- 日志记录
三、Spring Boot 创建拦截器
创建拦截器只需要实现 HandlerInterceptor 接口。
示例:
package com.example.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("请求路径:" + request.getRequestURI());
return true;
}
}
如果需要实现多个方法:
public class ApiInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
long start = System.currentTimeMillis();
request.setAttribute("startTime", start);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
long start = (long) request.getAttribute("startTime");
long cost = System.currentTimeMillis() - start;
System.out.println("接口耗时:" + cost + "ms");
}
}
四、注册拦截器
Spring Boot 中必须通过 WebMvcConfigurer 注册拦截器。
示例:
package com.example.config;
import com.example.interceptor.LogInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.addPathPatterns("/**")
.excludePathPatterns(
"/login",
"/static/**"
);
}
}
关键方法:
| 方法 | 作用 |
|---|---|
| addInterceptor | 注册拦截器 |
| addPathPatterns | 拦截路径 |
| excludePathPatterns | 排除路径 |
五、拦截路径规则详解
Spring MVC 拦截路径支持 Ant 风格表达式。
常见规则如下:
| 规则 | 含义 |
|---|---|
| /user | 精确匹配 |
| /user/* | 匹配一层 |
| /user/** | 匹配任意层 |
| /** | 匹配全部路径 |
示例:
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns(
"/api/login",
"/api/register"
);
说明:
/api/**拦截所有 API/api/login不拦截
六、拦截器执行顺序
当存在多个拦截器时,执行顺序由注册顺序决定。
示例:
registry.addInterceptor(new AInterceptor()).addPathPatterns("/**");
registry.addInterceptor(new BInterceptor()).addPathPatterns("/**");
执行顺序:
A.preHandle
B.preHandle
Controller
B.postHandle
A.postHandle
B.afterCompletion
A.afterCompletion
特点:
preHandle:顺序执行postHandle:逆序执行afterCompletion:逆序执行
七、典型应用场景
1 登录鉴权拦截器
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
HttpSession session = request.getSession(false);
if (session == null || session.getAttribute("user") == null) {
response.setStatus(401);
response.getWriter().write("未登录");
return false;
}
return true;
}
}
注册:
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns("/api/login");
2 API 请求日志记录
public class RequestLogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
long start = System.currentTimeMillis();
request.setAttribute("start", start);
System.out.println("请求:" + request.getMethod() +
" " + request.getRequestURI());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
long start = (long) request.getAttribute("start");
long cost = System.currentTimeMillis() - start;
System.out.println("耗时:" + cost + "ms");
}
}
3 Token 校验拦截器
public class TokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (token == null || !verifyToken(token)) {
response.setStatus(401);
response.getWriter().write("token 无效");
return false;
}
return true;
}
private boolean verifyToken(String token) {
return token.equals("test-token");
}
}
八、拦截器与 Filter 的区别
Spring Boot 中还存在 Servlet Filter,与拦截器功能类似,但实现层不同。
| 对比 | Filter | Interceptor |
|---|---|---|
| 规范 | Servlet 规范 | Spring MVC |
| 作用范围 | 所有请求 | 仅 Controller |
| 执行时机 | DispatcherServlet 之前 | Controller 之前 |
| 使用难度 | 较底层 | 更适合 Spring |
| 获取 Bean | 不方便 | 非常方便 |
执行顺序:
Filter
↓
DispatcherServlet
↓
Interceptor
↓
Controller
实际项目建议:
-
Filter:底层处理
- 编码
- 跨域
- 请求包装
-
Interceptor:业务逻辑
- 登录校验
- 权限验证
- 接口日志
九、Spring Boot 2 与 Spring Boot 3 区别
Spring Boot 2
包路径:
javax.servlet.*
示例:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
Spring Boot 3
由于升级到 Jakarta EE,包路径变为:
jakarta.servlet.*
示例:
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
这是最主要的变化。
拦截器接口本身没有变化:
HandlerInterceptor
仍然位于:
org.springframework.web.servlet
十、生产环境最佳实践
1 拦截器应保持轻量
拦截器运行在 所有请求路径上。
不应执行:
- 大量数据库查询
- 复杂计算
- 阻塞 IO
否则会显著影响接口性能。
2 使用 ThreadLocal 传递上下文
例如保存当前用户信息。
public class UserContext {
private static final ThreadLocal<Long> USER_ID = new ThreadLocal<>();
public static void set(Long userId) {
USER_ID.set(userId);
}
public static Long get() {
return USER_ID.get();
}
public static void clear() {
USER_ID.remove();
}
}
拦截器:
UserContext.set(userId);
afterCompletion:
UserContext.clear();
3 配合 Redis 做接口限流
例如:
用户访问接口
↓
拦截器
↓
Redis INCR
↓
超过阈值拒绝访问
示例:
String key = "rate:" + userId;
Long count = redisTemplate.opsForValue().increment(key);
if (count == 1) {
redisTemplate.expire(key, 1, TimeUnit.SECONDS);
}
if (count > 10) {
response.setStatus(429);
return false;
}
4 统一接口日志系统
生产环境通常将接口日志写入:
- Kafka
- Elasticsearch
- ClickHouse
结构示例:
| 字段 | 含义 |
|---|---|
| trace_id | 请求ID |
| uri | 请求路径 |
| method | HTTP方法 |
| user_id | 用户ID |
| cost | 耗时 |
| ip | 客户端IP |
十一、常见问题
1 静态资源被拦截
解决:
.excludePathPatterns(
"/css/**",
"/js/**",
"/images/**",
"/webjars/**"
)
2 拦截器无法注入 Bean
原因:
new LoginInterceptor()
不是 Spring Bean。
解决:
@Bean
public LoginInterceptor loginInterceptor() {
return new LoginInterceptor();
}
注册:
registry.addInterceptor(loginInterceptor());
3 @ResponseBody 返回被拦截
拦截器不会影响 JSON 返回。
但如果:
preHandle return false
必须手动写 response。
十二、完整项目结构示例
project
├─ config
│ └─ WebMvcConfig.java
│
├─ interceptor
│ ├─ LoginInterceptor.java
│ ├─ LogInterceptor.java
│ └─ TokenInterceptor.java
│
├─ controller
│ └─ UserController.java
│
└─ Application.java
Controller 示例:
@RestController
@RequestMapping("/api")
public class UserController {
@GetMapping("/test")
public String test() {
return "ok";
}
}
十三、拦截器执行流程总结
Spring Boot 请求执行流程:
HTTP Request
↓
Filter
↓
DispatcherServlet
↓
Interceptor.preHandle
↓
Controller
↓
Interceptor.postHandle
↓
View Render
↓
Interceptor.afterCompletion
↓
HTTP Response
拦截器是 Spring Web 开发中最重要的扩展机制之一,能够将大量通用逻辑从业务代码中抽离出来,实现:
- 代码解耦
- 统一控制
- 统一监控
- 统一安全策略
合理使用拦截器,可以显著提升系统架构的可维护性与扩展性。