Spring Boot拦截器详解与项目实战

一、拦截器概述与核心概念

Spring Boot拦截器(Interceptor)是基于Spring MVC框架的一种请求拦截机制,它允许开发者在请求处理的不同阶段插入自定义逻辑。拦截器主要作用于Controller层,能够在请求到达Controller之前、Controller方法执行之后以及整个请求完成之后执行特定操作。

拦截器与过滤器的区别主要体现在:

  • 所属框架:拦截器属于Spring MVC,而过滤器是Servlet规范的一部分
  • 作用范围:拦截器仅作用于Controller方法请求,过滤器作用于所有请求(包括静态资源)
  • 依赖注入:拦截器支持Spring依赖注入,过滤器不支持
  • 执行时机:过滤器先于拦截器执行,拦截器后于过滤器执行

拦截器的典型应用场景包括:

  • 登录验证与权限控制
  • 请求日志记录
  • 性能监控
  • 防止重复提交
  • 统一设置响应头信息

二、拦截器核心方法与执行流程

1. 拦截器三个核心方法

拦截器通过实现HandlerInterceptor接口并重写其三个核心方法:

java 复制代码
public interface HandlerInterceptor {
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    
    void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;
    
    void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
}

preHandle方法​:

  • 在Controller方法执行前调用
  • 返回true表示继续执行后续拦截器和目标方法,返回false则中断执行
  • 常用于权限校验、日志记录等

postHandle方法​:

  • 在Controller方法执行后、视图渲染前调用
  • 可修改ModelAndView对象
  • 使用频率较低

afterCompletion方法​:

  • 在整个请求完成后调用(视图渲染后)
  • 常用于资源清理、日志记录等

2. 拦截器执行流程

拦截器的完整执行顺序如下:

  1. 请求到达DispatcherServlet
  2. 拦截器1.preHandle() → 拦截器2.preHandle() → ... → 拦截器N.preHandle()
  3. 目标Controller方法执行
  4. 拦截器N.postHandle() ← ... ← 拦截器2.postHandle() ← 拦截器1.postHandle()
  5. 视图渲染
  6. 拦截器1.afterCompletion() ← ... ← 拦截器N.afterCompletion()

如果任一拦截器的preHandle方法返回false,则后续拦截器和目标方法都不会执行,但已执行的拦截器的afterCompletion方法仍会被调用。

三、拦截器实战配置

1. 创建自定义拦截器

创建一个实现HandlerInterceptor接口的拦截器类:

java 复制代码
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class AuthInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 登录校验逻辑
        String token = request.getHeader("Authorization");
        if(token == null || !token.equals("valid_token")) {
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write("{"code":401,"message":"未登录"}");
            return false;
        }
        return true;
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 可添加响应头等操作
        response.setHeader("X-Custom-Header", "interceptor-added");
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 资源清理或日志记录
        System.out.println("请求完成: " + request.getRequestURI());
    }
}

2. 注册拦截器

通过实现WebMvcConfigurer接口并重写addInterceptors方法来注册拦截器:

typescript 复制代码
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 WebConfig implements WebMvcConfigurer {
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthInterceptor())
                .addPathPatterns("/api/**") // 拦截路径
                .excludePathPatterns("/api/login", "/api/register"); // 排除路径
    }
}

3. 拦截规则配置

  • addPathPatterns():指定需要拦截的URL模式,支持Ant风格路径匹配
  • excludePathPatterns():指定不需要拦截的URL模式
  • 多个拦截器可通过order()方法设置执行顺序,数值越小优先级越高

四、项目实战案例

案例1:登录状态校验

java 复制代码
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute("userinfo") != null) {
            return true; // 已登录,放行
        }
        // 未登录,重定向到登录页
        response.sendRedirect("/user/login");
        return false;
    }
}

注册配置:

typescript 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/user/login", "/user/reg", "/**/*.css", "/**/*.jpg");
    }
}

案例2:请求日志记录与性能监控

java 复制代码
public class LogInterceptor implements HandlerInterceptor {
    private static final ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        startTimeThreadLocal.set(System.currentTimeMillis());
        System.out.println("【请求开始】IP: " + request.getRemoteAddr() + 
                         ", URI: " + request.getRequestURI());
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                              Object handler, Exception ex) throws Exception {
        long duration = System.currentTimeMillis() - startTimeThreadLocal.get();
        System.out.println("【请求结束】耗时: " + duration + "ms");
        startTimeThreadLocal.remove();
    }
}

案例3:防止重复提交

java 复制代码
public class RepeatSubmitInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getParameter("formToken");
        if (StringUtils.isEmpty(token) || RedisUtil.exists(token)) {
            response.getWriter().write("{"code":400,"message":"请勿重复提交"}");
            return false;
        }
        RedisUtil.set(token, "submitted", 60); // 设置60秒有效期
        return true;
    }
}

五、高级应用与最佳实践

1. 拦截器与AOP结合

对于更复杂的场景,可以结合拦截器和AOP实现更强大的功能:

less 复制代码
@Aspect
@Component
public class ApiLogAspect {
    @Around("@annotation(apiLog)")
    public Object around(ProceedingJoinPoint pjp, ApiLog apiLog) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = pjp.proceed();
        long cost = System.currentTimeMillis() - start;
        // 记录日志或执行其他操作
        return result;
    }
}

拦截器负责请求/响应包装,AOP负责业务逻辑处理,两者互补。

2. 静态资源放行

为避免拦截器影响静态资源访问,需配置排除路径:

typescript 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/", "/login", "/static/**", "/public/**");
    }
}

3. 性能优化建议

  • 避免在拦截器中执行耗时操作(如数据库查询、远程调用)
  • 对于需要异步处理的操作,使用线程池
  • 使用ThreadLocal时注意及时清理,防止内存泄漏

六、常见问题与解决方案

  1. 拦截器不生效

    • 检查是否添加了@Configuration注解
    • 确认拦截路径配置正确
    • 确保拦截器类被Spring管理(如添加@Component注解)
  2. 静态资源被拦截

    • 使用excludePathPatterns()排除静态资源路径
  3. 多个拦截器执行顺序问题

    • 通过order()方法明确指定执行顺序
  4. 获取请求Body内容

    • 使用ContentCachingRequestWrapper包装请求,解决流只能读取一次的问题

七、总结

Spring Boot拦截器是Web开发中强大的工具,通过合理使用可以:

  • 集中处理公共逻辑,减少代码冗余
  • 提高代码可维护性和可读性
  • 实现安全控制、日志记录等横切关注点

在实际项目中,应根据具体需求选择合适的拦截策略,并注意性能优化和资源管理。结合AOP等其他技术,可以构建更加灵活、强大的请求处理管道。

通过本文的讲解和实战案例,开发者应能够掌握Spring Boot拦截器的核心概念、使用方法和最佳实践,从而在实际项目中灵活应用。

相关推荐
间彧4 小时前
SpringBoot如何通过拦截器实现接口限流
后端
绝无仅有4 小时前
面试技巧之Linux相关问题的解答
后端·面试·github
绝无仅有4 小时前
某跳动大厂 MySQL 面试题解析与总结实战
后端·面试·github
道可到4 小时前
阿里面试原题 面试通关笔记05 | 异常、泛型与反射——类型擦除的成本与优化
java·后端·面试
贾维斯Echo4 小时前
保姆级教程!华为昇腾NPU DeepSeek安装部署全流程!
后端
Postkarte不想说话4 小时前
Xfce4 鼠标滚轮滚动禁止获取焦点
后端
风雨同舟的代码笔记4 小时前
Linux环境下MySQL安装教程
后端
风雨同舟的代码笔记4 小时前
Gradle 项目使用 MyBatis-Generator 自动生成代码:高效开发的利器
后端
风雨同舟的代码笔记4 小时前
DockerVS虚拟机:从架构师视角深度对比
后端