一、HandlerInterceptor核心定位与作用
HandlerInterceptor是Spring MVC框架中用于拦截HTTP请求的核心接口,它提供了对控制器方法执行的前后过程进行拦截的能力,是AOP思想在Web层的具体实现。
1.1 三大核心方法
public interface HandlerInterceptor {
// 控制器方法执行前调用
default boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {}
// 控制器方法执行后调用(视图渲染前)
default void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {}
// 请求完成后的回调(视图渲染后)
default void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {}
}
1.2 典型应用场景
- 权限验证:检查JWT/会话状态
- 日志记录:记录请求参数和响应时间
- 数据预处理:设置线程本地变量
- 性能监控:计算接口耗时
- 通用参数处理:解析多租户ID等
二、底层实现机制深度剖析
2.1 Spring MVC请求处理流程
HTTP Request → DispatcherServlet → HandlerMapping →
HandlerExecutionChain(包含Interceptors) → HandlerAdapter →
Controller → ModelAndView → ViewResolver →
View Render → Response
2.2 拦截器执行时序图
三、完整代码实现与解析
3.1 基础拦截器实现
/**
* 认证拦截器
*/
public class AuthInterceptor implements HandlerInterceptor {
private static final Logger log = LoggerFactory.getLogger(AuthInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (!validateToken(token)) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter().write("{\"code\":401,\"message\":\"未授权\"}");
return false; // 中断请求
}
// 将用户信息存入请求属性
request.setAttribute("currentUser", parseUser(token));
return true; // 继续执行
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
// 可修改ModelAndView
if (modelAndView != null) {
modelAndView.addObject("timestamp", System.currentTimeMillis());
}
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
// 资源清理
UserContextHolder.clear();
// 记录请求完成日志
long startTime = (Long) request.getAttribute("startTime");
log.info("请求 {} 耗时 {}ms",
request.getRequestURI(),
System.currentTimeMillis() - startTime);
}
private boolean validateToken(String token) {
// 实现实际的token验证逻辑
return StringUtils.hasText(token);
}
}
3.2 注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/api/**") // 拦截路径
.excludePathPatterns("/api/public/**"); // 排除路径
registry.addInterceptor(new LoggingInterceptor())
.order(1) // 执行顺序
.addPathPatterns("/**");
}
}
四、高级应用场景
4.1 多租户数据隔离
public class TenantInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String tenantId = request.getHeader("X-Tenant-ID");
if (StringUtils.isEmpty(tenantId)) {
tenantId = request.getParameter("tenantId");
}
if (!TenantContext.isValidTenant(tenantId)) {
throw new TenantException("无效的租户ID");
}
TenantContext.setCurrentTenant(tenantId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
TenantContext.clear();
}
}
4.2 接口耗时监控
public class MetricsInterceptor implements HandlerInterceptor {
private final MeterRegistry meterRegistry;
public MetricsInterceptor(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
request.setAttribute("startTime", System.currentTimeMillis());
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;
String uri = request.getRequestURI();
String method = request.getMethod();
int status = response.getStatus();
meterRegistry.timer("http.requests")
.tags("uri", uri, "method", method, "status", String.valueOf(status))
.record(duration, TimeUnit.MILLISECONDS);
}
}
五、与过滤器的对比
特性 | HandlerInterceptor | Filter |
---|---|---|
容器级别 | Spring容器管理 | Servlet容器管理 |
执行时机 | Controller方法前后 | Servlet方法前后 |
依赖注入 | 支持 | 不支持(需通过FilterProxy) |
访问控制器信息 | 可以获取HandlerMethod | 无法获取 |
执行顺序 | 在Filter之后 | 最先执行 |
异常处理 | 可以结合@ExceptionHandler | 只能自行处理 |
六、最佳实践与性能优化
6.1 拦截器设计原则
- 单一职责:每个拦截器只关注一个功能点
- 快速失败:preHandle中尽早返回false终止无效请求
- 避免阻塞:不在拦截器中执行耗时操作
- 线程安全:注意成员变量的并发访问
6.2 性能优化建议
public class CachingInterceptor implements HandlerInterceptor {
// 使用ConcurrentHashMap实现简单缓存
private final ConcurrentMap<String, Object> cache = new ConcurrentHashMap<>();
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String cacheKey = buildCacheKey(request);
Object cachedData = cache.get(cacheKey);
if (cachedData != null) {
writeJsonResponse(response, cachedData);
return false; // 缓存命中,不再执行控制器
}
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
if (modelAndView != null) {
String cacheKey = buildCacheKey(request);
cache.put(cacheKey, modelAndView.getModel());
}
}
// 构建缓存键
private String buildCacheKey(HttpServletRequest request) {
return request.getRequestURI() + "?" + request.getQueryString();
}
}
七、常见问题解决方案
7.1 拦截器不生效排查
- 检查注册路径:确认addPathPatterns包含目标路径
- 验证顺序问题:多个拦截器时注意order值
- 确认配置加载:确保@Configuration类被扫描到
- 检查Filter阻断:某些Filter可能提前返回响应
7.2 异步请求处理
对于异步请求(DeferredResult/Callable),需要实现AsyncHandlerInterceptor:
public class AsyncInterceptor implements AsyncHandlerInterceptor {
@Override
public void afterConcurrentHandlingStarted(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 异步请求开始时调用
}
}
7.3 与Swagger集成
避免拦截器拦截Swagger文档请求:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.excludePathPatterns("/swagger-resources/**")
.excludePathPatterns("/v2/api-docs")
.excludePathPatterns("/webjars/**")
.excludePathPatterns("/swagger-ui.html");
}
八、生产环境实战案例
8.1 请求限流拦截器
public class RateLimitInterceptor implements HandlerInterceptor {
private final RateLimiter rateLimiter;
public RateLimitInterceptor(int permitsPerSecond) {
this.rateLimiter = RateLimiter.create(permitsPerSecond);
}
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if (!rateLimiter.tryAcquire()) {
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
response.setHeader("Retry-After", "60");
response.getWriter().write("{\"code\":429,\"message\":\"请求过于频繁\"}");
return false;
}
return true;
}
}
8.2 数据解密拦截器
public class DecryptInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
if (handlerMethod.hasMethodAnnotation(DecryptRequest.class)) {
// 读取并解密请求体
String encrypted = request.getReader().lines()
.collect(Collectors.joining());
String decrypted = decrypt(encrypted);
// 替换请求体
byte[] bytes = decrypted.getBytes(StandardCharsets.UTF_8);
request = new ContentCachingRequestWrapper(request, bytes);
request.setAttribute("decryptedBody", decrypted);
}
}
return true;
}
private String decrypt(String content) {
// 实现实际解密逻辑
return SecurityUtils.decrypt(content);
}
}
HandlerInterceptor是Spring MVC中强大的拦截机制,合理使用可以实现横切关注点的统一处理,同时保持业务代码的整洁性。掌握其原理和高级用法,能够显著提升Web应用的可维护性和扩展性。