拦截器(Interceptor)是Spring MVC框架中的一种核心组件,它可以在请求到达Controller之前和视图渲染之后对请求进行拦截处理。与过滤器(Filter)不同,拦截器工作在Spring的上下文环境中,能够直接使用Spring的依赖注入等功能。
特性 | 拦截器(Interceptor) | 过滤器(Filter) |
---|---|---|
工作层次 | Spring MVC层面 | Servlet容器层面 |
依赖关系 | 依赖Spring容器 | 不依赖Spring |
执行时机 | Controller方法前后 | Servlet请求处理前后 |
访问对象 | 可以获取HandlerMethod信息 | 只能获取Servlet API对象 |
异常处理 | 可以接入Spring的异常处理机制 | 无法使用Spring的异常处理 |
配置方式 | 实现接口+注册 | @WebFilter或web.xml配置 |
实现HandlerInterceptor
接口的三个核心方法:
@Component
public class CustomInterceptor implements HandlerInterceptor {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 预处理回调方法(Controller方法执行前)
* 返回true表示继续流程,false表示中断
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
logger.info(">>> preHandle: {}", request.getRequestURI());
// 示例:验证Token
String token = request.getHeader("Authorization");
if (!isValidToken(token)) {
response.sendError(HttpStatus.UNAUTHORIZED.value(), "Invalid token");
return false;
}
return true;
}
/**
* 后处理回调方法(Controller方法执行后,视图渲染前)
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
logger.info(">>> postHandle: {}", request.getRequestURI());
// 可修改ModelAndView
if (modelAndView != null) {
modelAndView.addObject("interceptorMsg", "Processed by interceptor");
}
}
/**
* 整个请求完成后的回调方法(视图渲染完成后)
* 适合进行资源清理
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
logger.info(">>> afterCompletion: {}", request.getRequestURI());
// 记录请求耗时等
long startTime = (Long) request.getAttribute("startTime");
logger.info("Request '{}' completed in {}ms",
request.getRequestURI(),
System.currentTimeMillis() - startTime);
// 异常处理
if (ex != null) {
logger.error("Request processing failed", ex);
}
}
private boolean isValidToken(String token) {
// 实际项目中应实现真正的token验证逻辑
return token != null && token.startsWith("Bearer ");
}
}
通过WebMvcConfigurer
配置拦截器的拦截规则:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private CustomInterceptor customInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customInterceptor)
.addPathPatterns("/api/**") // 拦截路径
.excludePathPatterns("/api/public/**") // 排除路径
.order(1); // 拦截器执行顺序
// 可以注册多个拦截器
registry.addInterceptor(new LoggingInterceptor())
.addPathPatterns("/**")
.order(2);
}
}
当配置多个拦截器时,执行顺序如下:
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
if (ex instanceof BusinessException) {
response.resetBuffer();
response.setStatus(HttpStatus.BAD_REQUEST.value());
response.setContentType("application/json");
response.getWriter().write("{\"error\":\"" + ex.getMessage() + "\"}");
}
}
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
@ResponseBody
public ResponseEntity<?> handleBusinessException(BusinessException ex) {
return ResponseEntity.badRequest().body(ex.getMessage());
}
}
对于异步请求(Callable
/DeferredResult
),需要实现AsyncHandlerInterceptor
:
@Component
public class AsyncInterceptor implements AsyncHandlerInterceptor {
@Override
public void afterConcurrentHandlingStarted(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 异步请求开始时调用(代替postHandle和afterCompletion)
System.out.println("Async request started");
}
}
public class AuthInterceptor implements HandlerInterceptor {
@Autowired
private AuthService authService;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 检查是否需要登录
if (handlerMethod.hasMethodAnnotation(RequiresLogin.class)) {
String token = request.getHeader("X-Token");
if (!authService.validateToken(token)) {
throw new UnauthorizedException("请先登录");
}
}
// 检查权限
RequiresPermission permission = handlerMethod.getMethodAnnotation(RequiresPermission.class);
if (permission != null) {
String[] required = permission.value();
User user = authService.getCurrentUser();
if (!user.hasPermissions(required)) {
throw new ForbiddenException("权限不足");
}
}
return true;
}
}
public class LoggingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
request.setAttribute("startTime", System.currentTimeMillis());
logRequest(request);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
long duration = System.currentTimeMillis() - (Long) request.getAttribute("startTime");
logResponse(request, response, duration, ex);
}
private void logRequest(HttpServletRequest request) {
String queryString = request.getQueryString();
String path = queryString == null ? request.getRequestURI() :
request.getRequestURI() + "?" + queryString;
Logger.info("Request [{} {}] from IP: {}",
request.getMethod(),
path,
request.getRemoteAddr());
}
private void logResponse(HttpServletRequest request,
HttpServletResponse response,
long duration,
Exception ex) {
Logger.info("Response [{} {}] status: {} duration: {}ms",
request.getMethod(),
request.getRequestURI(),
response.getStatus(),
duration);
}
}
public class RateLimitInterceptor implements HandlerInterceptor {
private final RateLimiter rateLimiter = RateLimiter.create(100); // 每秒100个请求
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if (!rateLimiter.tryAcquire()) {
response.sendError(HttpStatus.TOO_MANY_REQUESTS.value(), "请求过于频繁");
return false;
}
return true;
}
}
检查步骤:
@Component
注解@Configuration
且实现了WebMvcConfigurer
解决方案:
@Component
)@Component
public class AuthInterceptor implements HandlerInterceptor {
private final AuthService authService;
@Autowired // 构造器注入
public AuthInterceptor(AuthService authService) {
this.authService = authService;
}
}
解决方案:
registry.addInterceptor(interceptor)
.excludePathPatterns("/static/**", "/public/**");
/**
这样的宽泛匹配Spring Boot 3.x推荐使用@Configuration
结合@Bean
方式:
@Configuration
public class WebConfig {
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CustomInterceptor());
}
};
}
}
Spring 6.x引入了更简洁的函数式注册方式:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(request -> {
// preHandle逻辑
return true;
})
.addInterceptor((request, response, handler) -> {
// postHandle逻辑
})
.addInterceptor((request, response, handler, ex) -> {
// afterCompletion逻辑
});
}
}
场景 | 推荐实现方式 | 注意事项 |
---|---|---|
认证授权 | preHandle中进行校验 | 注意排除登录/公开接口 |
日志记录 | preHandle+afterCompletion组合 | 异步处理耗时日志操作 |
参数预处理 | preHandle中修改request参数 | 注意线程安全问题 |
接口限流 | preHandle中使用RateLimiter | 快速失败,避免阻塞线程 |
响应统一包装 | postHandle中修改ModelAndView | 仅适用于返回视图的场景 |
性能监控 | 记录请求开始和结束时间 | 使用System.nanoTime()更精确 |
分层设计:
配置管理:
# application.yml
interceptor:
auth:
enable: true
exclude-paths: /public/**,/health
log:
enable: true
动态启用/禁用:
@ConditionalOnProperty(name = "interceptor.auth.enable", havingValue = "true")
@Bean
public AuthInterceptor authInterceptor() {
return new AuthInterceptor();
}
测试策略:
@Test
void testAuthInterceptor() throws Exception {
mockMvc.perform(get("/api/secured"))
.andExpect(status().isUnauthorized());
}
拦截器作为Spring MVC的核心扩展点,合理使用可以极大提高代码的复用性和可维护性。掌握其原理和最佳实践,能够帮助开发者构建更加健壮和灵活的Web应用程序。