Spring Boot中拦截器的使用指南:从入门到精通

拦截器(Interceptor)是Spring MVC中非常重要的组件,它可以在请求处理的不同阶段进行拦截和处理。在Spring Boot中使用拦截器非常简单,下面我将详细介绍完整的使用方法和最佳实践。

一、拦截器基础概念

1. 拦截器 vs 过滤器

特性拦截器(Interceptor)过滤器(Filter)
作用范围Spring MVC框架内Servlet容器层面
依赖依赖Spring容器不依赖Spring容器
实现方式实现HandlerInterceptor接口实现javax.servlet.Filter接口
获取信息能力可以获取处理器方法信息、ModelAndView等只能获取基本的Servlet API信息

2. 拦截器执行时机

Client DispatcherServlet Interceptor Controller 发送请求 preHandle() true/false 执行控制器方法 返回结果 postHandle() 返回响应 afterCompletion() 直接返回 alt [返回true] [返回false] Client DispatcherServlet Interceptor Controller

二、创建自定义拦截器

1. 基本实现步骤

@Component
public class CustomInterceptor implements HandlerInterceptor {
    
    // 在控制器方法执行前调用
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        System.out.println("preHandle方法执行");
        // 可以进行权限验证、日志记录等
        return true; // 返回true继续执行,false则中断
    }
    
    // 在控制器方法执行后,视图渲染前调用
    @Override
    public void postHandle(HttpServletRequest request, 
                         HttpServletResponse response, 
                         Object handler,
                         ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法执行");
    }
    
    // 在整个请求完成后调用(视图已渲染完成)
    @Override
    public void afterCompletion(HttpServletRequest request, 
                               HttpServletResponse response, 
                               Object handler, 
                               Exception ex) throws Exception {
        System.out.println("afterCompletion方法执行");
    }
}

2. 注册拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Autowired
    private CustomInterceptor customInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(customInterceptor)
                .addPathPatterns("/**")          // 拦截所有路径
                .excludePathPatterns("/login");  // 排除登录路径
        
        // 可以添加多个拦截器,按添加顺序执行
        registry.addInterceptor(new AnotherInterceptor())
                .addPathPatterns("/admin/**");   // 只拦截admin路径
    }
}

三、拦截器高级用法

1. 多拦截器执行顺序

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 拦截器1 - 最先执行preHandle,最后执行afterCompletion
        registry.addInterceptor(new Interceptor1())
                .order(1);
                
        // 拦截器2 - 后执行preHandle,先执行afterCompletion
        registry.addInterceptor(new Interceptor2())
                .order(2);
    }
}

执行顺序:

preHandle 1 → preHandle 2 → controller → postHandle 2 → postHandle 1 → afterCompletion 2 → afterCompletion 1

2. 获取请求参数和处理器信息

@Override
public boolean preHandle(HttpServletRequest request, 
                       HttpServletResponse response, 
                       Object handler) throws Exception {
    
    // 获取请求参数
    String param = request.getParameter("param");
    
    // 判断handler类型
    if (handler instanceof HandlerMethod) {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        
        // 获取控制器类和方法信息
        Class<?> controllerClass = handlerMethod.getBeanType();
        Method method = handlerMethod.getMethod();
        
        // 获取方法上的注解
        GetMapping getMapping = method.getAnnotation(GetMapping.class);
    }
    
    return true;
}

3. 拦截器中注入Service

@Component
public class AuthInterceptor implements HandlerInterceptor {
    
    // 可以直接注入Spring管理的Bean
    @Autowired
    private UserService userService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        String token = request.getHeader("Authorization");
        if (!userService.validateToken(token)) {
            response.sendError(HttpStatus.UNAUTHORIZED.value(), "无效token");
            return false;
        }
        return true;
    }
}

四、实战案例

1. 登录认证拦截器

@Component
public class LoginInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        
        HttpSession session = request.getSession();
        Object user = session.getAttribute("user");
        
        if (user == null) {
            // 未登录,重定向到登录页
            response.sendRedirect("/login");
            return false;
        }
        
        return true;
    }
}

2. 接口耗时统计拦截器

@Component
public class TimeCostInterceptor implements HandlerInterceptor {
    
    private static final ThreadLocal<Long> timeHolder = new ThreadLocal<>();
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        timeHolder.set(System.currentTimeMillis());
        return true;
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, 
                              HttpServletResponse response, 
                              Object handler, 
                              Exception ex) throws Exception {
        long startTime = timeHolder.get();
        long cost = System.currentTimeMillis() - startTime;
        System.out.println("请求耗时: " + cost + "ms");
        timeHolder.remove();
    }
}

3. 全局异常处理拦截器

@Component
public class ExceptionInterceptor implements HandlerInterceptor {
    
    @Override
    public void afterCompletion(HttpServletRequest request, 
                              HttpServletResponse response, 
                              Object handler, 
                              Exception ex) throws Exception {
        if (ex != null) {
            // 记录异常日志
            log.error("请求处理异常", ex);
            
            // 统一异常处理
            response.setContentType("application/json");
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            response.getWriter().write("{\"code\":500,\"message\":\"服务异常\"}");
        }
    }
}

五、常见问题解决方案

1. 拦截器不生效问题排查

  1. 检查是否注册:确认在配置类中调用了addInterceptor
  2. 检查路径匹配:确认addPathPatternsexcludePathPatterns配置正确
  3. 检查顺序问题:其他拦截器可能中断了请求传递
  4. 检查Spring Boot版本:不同版本可能有差异

2. 静态资源被拦截问题

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new MyInterceptor())
            .addPathPatterns("/**")
            .excludePathPatterns("/static/**", "/resources/**");
}

3. 拦截器中获取RequestBody

由于请求体只能读取一次,需要在过滤器中先缓存:

public class ContentCachingFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                   HttpServletResponse response, 
                                   FilterChain filterChain) throws IOException, ServletException {
        ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
        filterChain.doFilter(wrappedRequest, response);
    }
}

然后在拦截器中:

@Override
public boolean preHandle(HttpServletRequest request, 
                       HttpServletResponse response, 
                       Object handler) throws Exception {
    if (request instanceof ContentCachingRequestWrapper) {
        ContentCachingRequestWrapper wrappedRequest = (ContentCachingRequestWrapper) request;
        byte[] body = wrappedRequest.getContentAsByteArray();
        String requestBody = new String(body, StandardCharsets.UTF_8);
        // 使用requestBody
    }
    return true;
}

六、最佳实践建议

  1. 职责单一:每个拦截器只关注一个功能点
  2. 性能考虑:避免在拦截器中做耗时操作
  3. 合理排序:认证拦截器应该放在最前面
  4. 异常处理:注意拦截器中的异常处理逻辑
  5. 避免循环:拦截器逻辑不应导致循环重定向

七、总结

Spring Boot中的拦截器是一个非常强大的工具,合理使用可以:

  • 实现统一的权限控制
  • 记录请求日志
  • 统计接口耗时
  • 全局异常处理
  • 参数预处理等

通过本文的介绍,你应该已经掌握了Spring Boot拦截器的完整使用方法。实际项目中可以根据需求灵活组合多个拦截器,构建强大的请求处理管道。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北辰alk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值