Spring Boot拦截器(Interceptor)深度应用指南

一、拦截器基础概念

1.1 什么是拦截器?

拦截器(Interceptor)是Spring MVC框架中的一种核心组件,它可以在请求到达Controller之前和视图渲染之后对请求进行拦截处理。与过滤器(Filter)不同,拦截器工作在Spring的上下文环境中,能够直接使用Spring的依赖注入等功能。

1.2 拦截器 vs 过滤器

特性 拦截器(Interceptor) 过滤器(Filter)
工作层次 Spring MVC层面 Servlet容器层面
依赖关系 依赖Spring容器 不依赖Spring
执行时机 Controller方法前后 Servlet请求处理前后
访问对象 可以获取HandlerMethod信息 只能获取Servlet API对象
异常处理 可以接入Spring的异常处理机制 无法使用Spring的异常处理
配置方式 实现接口+注册 @WebFilter或web.xml配置

二、拦截器核心实现

2.1 创建自定义拦截器

实现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 ");
    }
}

2.2 注册拦截器

通过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);
    }
}

三、拦截器高级应用

3.1 多拦截器执行顺序

当配置多个拦截器时,执行顺序如下:

  1. preHandle:按order值从小到大顺序执行
  2. Controller方法:所有preHandle返回true后执行
  3. postHandle:按order值从大到小逆序执行
  4. afterCompletion:按order值从大到小逆序执行

3.2 拦截器与异常处理

方案1:在afterCompletion中处理
@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() + "\"}");
    }
}
方案2:结合@ControllerAdvice
@ControllerAdvice
public class GlobalExceptionHandler {
    
    
    
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public ResponseEntity<?> handleBusinessException(BusinessException ex) {
    
    
        return ResponseEntity.badRequest().body(ex.getMessage());
    }
}

3.3 拦截器与异步请求

对于异步请求(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");
    }
}

四、实战应用场景

4.1 认证与授权拦截

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;
    }
}

4.2 请求日志记录

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);
    }
}

4.3 接口限流控制

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;
    }
}

五、性能优化与最佳实践

5.1 拦截器优化技巧

  1. 轻量级preHandle:preHandle方法应尽可能高效,避免复杂逻辑
  2. 合理设置拦截路径:精确配置addPathPatterns,避免拦截不必要请求
  3. 异步处理耗时操作:如日志记录等操作可以放入线程池异步执行
  4. 使用@Order注解:明确指定拦截器顺序,避免依赖不可控的注册顺序

5.2 常见问题解决方案

问题1:拦截器不生效

检查步骤

  1. 确认拦截器类有@Component注解
  2. 确认配置类有@Configuration且实现了WebMvcConfigurer
  3. 检查拦截路径(addPathPatterns)是否正确
  4. 查看是否有更高优先级的拦截器中断了请求
问题2:拦截器中注入Bean为null

解决方案

  1. 确保拦截器本身是Spring管理的Bean(添加@Component
  2. 不要在new创建的拦截器实例中使用自动注入
  3. 通过构造器注入代替字段注入:
@Component
public class AuthInterceptor implements HandlerInterceptor {
    
    
    
    private final AuthService authService;
    
    @Autowired  // 构造器注入
    public AuthInterceptor(AuthService authService) {
    
    
        this.authService = authService;
    }
}
问题3:拦截静态资源

解决方案

  1. 明确排除静态资源路径:
    registry.addInterceptor(interceptor)
            .excludePathPatterns("/static/**", "/public/**");
    
  2. 或者精确配置拦截路径,避免/**这样的宽泛匹配

六、Spring Boot 3.x新特性

6.1 拦截器注册新方式

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());
            }
        };
    }
}

6.2 函数式编程支持

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逻辑
                });
    }
}

七、总结与最佳实践

7.1 拦截器适用场景总结

场景 推荐实现方式 注意事项
认证授权 preHandle中进行校验 注意排除登录/公开接口
日志记录 preHandle+afterCompletion组合 异步处理耗时日志操作
参数预处理 preHandle中修改request参数 注意线程安全问题
接口限流 preHandle中使用RateLimiter 快速失败,避免阻塞线程
响应统一包装 postHandle中修改ModelAndView 仅适用于返回视图的场景
性能监控 记录请求开始和结束时间 使用System.nanoTime()更精确

7.2 项目实战建议

  1. 分层设计

    • 基础拦截器:处理日志、监控等通用逻辑
    • 业务拦截器:处理认证、权限等业务逻辑
  2. 配置管理

    # application.yml
    interceptor:
      auth:
        enable: true
        exclude-paths: /public/**,/health
      log:
        enable: true
    
  3. 动态启用/禁用

    @ConditionalOnProperty(name = "interceptor.auth.enable", havingValue = "true")
    @Bean
    public AuthInterceptor authInterceptor() {
          
          
        return new AuthInterceptor();
    }
    
  4. 测试策略

    • 单元测试:直接测试拦截器逻辑
    • 集成测试:使用MockMvc测试拦截链
    @Test
    void testAuthInterceptor() throws Exception {
          
          
        mockMvc.perform(get("/api/secured"))
               .andExpect(status().isUnauthorized());
    }
    

拦截器作为Spring MVC的核心扩展点,合理使用可以极大提高代码的复用性和可维护性。掌握其原理和最佳实践,能够帮助开发者构建更加健壮和灵活的Web应用程序。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_16242613/article/details/147016681
今日推荐