Spring Boot 拦截器使用全指南 | 详细教程与示例代码
- 发布时间:2026-03-09 11:42:06
- 本文热度:浏览 7 赞 0 评论 0
- 文章标签: Spring Boot Java Web Development
- 全文共1字,阅读约需1分钟
在Spring Boot应用中,拦截器(Interceptor)是一种强大的机制,用于在请求处理的生命周期中插入自定义逻辑,如日志记录、权限验证或性能监控。拦截器基于Spring MVC框架,允许开发者在控制器方法执行前后介入处理,而无需修改业务代码。与过滤器(Filter)不同,拦截器专注于Spring MVC的请求处理流程,提供更细粒度的控制。本文将深入探讨拦截器的概念、实现方式、常见用例及最佳实践,帮助你全面掌握其在Spring Boot中的应用。
什么是拦截器?
拦截器是Spring MVC的核心组件,实现了HandlerInterceptor接口。它在请求到达控制器前、控制器执行后,以及请求完成后的不同阶段执行自定义代码。拦截器的主要目的是实现横切关注点(Cross-Cutting Concerns),如安全检查或日志记录,而无需在多个控制器中重复代码。
拦截器 vs. 过滤器
理解拦截器前,需区分它与Servlet过滤器的区别:
- 作用范围:过滤器作用于整个Servlet生命周期(包括静态资源),而拦截器仅针对Spring MVC处理的请求(如Controller方法)。
- 执行时机:过滤器在请求进入Servlet容器时触发;拦截器在Spring MVC的DispatcherServlet处理请求时介入。
- 依赖关系:拦截器依赖于Spring上下文,可访问Bean和注解;过滤器独立于Spring,无法直接使用Spring功能。
例如,如果你需要记录Controller方法的执行时间,拦截器是理想选择。但如果要处理所有HTTP请求(包括静态文件),则使用过滤器更合适。
为什么使用拦截器?
拦截器提供以下优势:
- 代码重用:将通用逻辑(如认证)集中到拦截器,避免控制器代码膨胀。
- 灵活性:可在请求处理的不同阶段(preHandle, postHandle, afterCompletion)注入逻辑。
- 集成性:与Spring生态无缝集成,支持依赖注入和AOP。
- 性能优化:轻量级实现,不影响应用性能。
常见使用场景包括:
- 日志记录:记录请求参数、响应时间。
- 权限控制:验证用户权限,拒绝未授权请求。
- 跨域处理:设置CORS头。
- 性能监控:测量方法执行耗时。
- 输入验证:在控制器前校验参数。
Spring Boot中实现拦截器
在Spring Boot中,实现拦截器需两步:创建拦截器类和注册拦截器。Spring Boot自动配置了Spring MVC,简化了这一过程。
步骤1: 创建拦截器类
拦截器必须实现HandlerInterceptor接口,该接口定义了三个方法:
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler):在控制器方法执行前调用。返回boolean:true继续流程,false中断请求(常用于权限拒绝)。postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView):在控制器方法执行后、视图渲染前调用。可修改ModelAndView。afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex):在请求完成后调用,用于资源清理或异常处理。
以下是一个基础拦截器示例,记录请求信息:
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class LoggingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
System.out.println("Request URL: " + request.getRequestURL());
System.out.println("Start Time: " + startTime);
return true; // 继续执行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
long startTime = (Long) request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
long executeTime = endTime - startTime;
System.out.println("Request processed in: " + executeTime + "ms");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
if (ex != null) {
System.out.println("Request failed with exception: " + ex.getMessage());
} else {
System.out.println("Request completed successfully");
}
}
}
此拦截器在preHandle中记录请求开始时间,在postHandle中计算执行时间,在afterCompletion中处理异常。@Component注解使其成为Spring Bean,便于依赖注入。
步骤2: 注册拦截器
创建拦截器后,需通过配置类注册到Spring MVC。使用WebMvcConfigurer接口的addInterceptors方法:
import org.springframework.beans.factory.annotation.Autowired;
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 InterceptorConfig implements WebMvcConfigurer {
@Autowired
private LoggingInterceptor loggingInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingInterceptor)
.addPathPatterns("/**") // 应用于所有路径
.excludePathPatterns("/static/**"); // 排除静态资源
}
}
这里,InterceptorRegistry用于添加拦截器并指定路径模式。addPathPatterns("/**")表示拦截所有请求,excludePathPatterns排除特定路径(如静态文件)。
详细解释拦截器方法
每个HandlerInterceptor方法有特定用途和参数,理解它们对有效使用拦截器至关重要。
preHandle 方法
preHandle在控制器方法执行前调用,是最常用的方法。
- 参数:
HttpServletRequest request:当前HTTP请求对象。HttpServletResponse response:HTTP响应对象,可用于设置头或状态码。Object handler:处理请求的处理器(通常是HandlerMethod)。
- 返回值:
boolean。返回true继续流程;返回false中断请求,常用于权限验证失败。 - 用途:执行前置检查,如认证或参数验证。
- 示例:实现一个简单认证拦截器:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String authToken = request.getHeader("Authorization");
if (authToken == null || !authToken.equals("valid-token")) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
return false; // 中断请求
}
return true; // 继续
}
此代码检查Authorization头,无效则返回401错误。
postHandle 方法
postHandle在控制器方法执行后调用,但在视图渲染前。
- 参数:包括
ModelAndView modelAndView,可修改模型数据或视图名称。 - 返回值:无。
- 用途:修改响应数据或添加通用属性。
- 示例:添加全局属性到模型:
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if (modelAndView != null) {
modelAndView.addObject("serverTime", new Date());
}
}
这为所有视图添加当前服务器时间。
afterCompletion 方法
afterCompletion在请求完成后调用,无论是否异常。
- 参数:包括
Exception ex,捕获控制器抛出的异常。 - 返回值:无。
- 用途:资源清理或异常日志记录。
- 示例:记录异常信息:
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
if (ex != null) {
logger.error("Request error: ", ex);
}
}
常见用例与代码示例
拦截器适用于多种场景。以下是几个实用例子,每个都附完整代码。
用例1: 日志记录拦截器
记录请求详情和执行时间,帮助调试和监控。
@Component
public class PerformanceLoggingInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(PerformanceLoggingInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
request.setAttribute("startTime", System.currentTimeMillis());
logger.info("Request started: {} {}", request.getMethod(), request.getRequestURI());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
long startTime = (Long) request.getAttribute("startTime");
long duration = System.currentTimeMillis() - startTime;
logger.info("Request completed: {}ms, Status: {}", duration, response.getStatus());
}
}
注册时,可排除健康检查端点:
registry.addInterceptor(performanceLoggingInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/actuator/health");
用例2: 认证拦截器
验证JWT令牌,保护敏感API。
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Autowired
private JwtUtil jwtUtil; // 假设JwtUtil是自定义工具类
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Missing token");
return false;
}
try {
String username = jwtUtil.validateToken(token.substring(7));
request.setAttribute("username", username); // 存储用户信息供控制器使用
return true;
} catch (Exception e) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid token");
return false;
}
}
}
此拦截器提取并验证JWT,无效则拒绝请求。
用例3: 跨域拦截器
设置CORS头,解决跨域问题。
@Component
public class CorsInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
return true;
}
}
虽然Spring Boot有内置CORS配置,但拦截器提供更细粒度控制。
高级主题
掌握基础后,探索高级用法能提升拦截器效能。
多个拦截器的顺序
当注册多个拦截器时,执行顺序由注册顺序决定。preHandle按注册顺序执行,postHandle和afterCompletion逆序执行。
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingInterceptor).order(1); // 先执行
registry.addInterceptor(authInterceptor).order(2); // 后执行
}
顺序重要:例如,先日志后认证,确保日志记录所有请求。
路径模式匹配
使用Ant风格路径模式:
addPathPatterns("/api/**"):匹配所有/api路径。excludePathPatterns("/public/**"):排除公共路径。 支持通配符:*(单段路径),**(多段路径)。
使用注解驱动拦截器
结合自定义注解,实现更灵活的控制。例如,创建@RequireAuth注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireAuth {
}
在拦截器中检查注解:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
if (handlerMethod.getMethodAnnotation(RequireAuth.class) != null) {
// 执行认证逻辑
}
}
return true;
}
然后在控制器方法上使用@RequireAuth,仅拦截带注解的方法。
异常处理
在afterCompletion中处理异常,但注意:拦截器不替代全局异常处理器(@ControllerAdvice)。结合使用:
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
if (ex != null) {
// 记录日志,但实际处理交给@ControllerAdvice
}
}
最佳实践
遵循这些实践确保拦截器高效可靠:
- 保持轻量:避免在拦截器中执行耗时操作(如数据库查询),以免影响性能。
- 单一职责:每个拦截器只处理一个关注点(如只做日志或只做认证)。
- 异常处理:在
afterCompletion中记录异常,但用全局机制处理。 - 路径排除:总是排除静态资源或不需拦截的路径。
- 测试覆盖:编写单元测试验证拦截器行为。
- 避免状态存储:不在拦截器中存储请求状态;用
request.setAttribute传递数据。
测试拦截器
测试是确保拦截器正确的关键。使用Spring的MockMvc模拟HTTP请求:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
public class AuthInterceptorTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testUnauthorizedAccess() throws Exception {
mockMvc.perform(get("/secure"))
.andExpect(status().isUnauthorized());
}
@Test
public void testAuthorizedAccess() throws Exception {
mockMvc.perform(get("/secure").header("Authorization", "Bearer valid-token"))
.andExpect(status().isOk());
}
}
此测试验证认证拦截器:无令牌时返回401,有有效令牌时通过。
常见问题与解决
- 拦截器不生效:检查是否注册正确;确保路径模式匹配;确认类上有
@Component和配置类有@Configuration。 - 顺序问题:多个拦截器时,显式设置
order值。 - 性能影响:优化逻辑,避免阻塞操作。
- 与过滤器冲突:如果同时使用过滤器和拦截器,确保它们逻辑不重叠。
结论
Spring Boot拦截器是处理横切关注点的强大工具,通过实现HandlerInterceptor接口和注册配置,可轻松集成日志、认证等功能。本文覆盖了从基础概念到高级用法的全指南,包括代码示例、测试方法和最佳实践。掌握这些,你将能构建更健壮、可维护的Spring Boot应用。记住,拦截器应与Spring的其他特性(如AOP)结合,以实现最佳架构。