当前位置: 首页 > news >正文

Spring Boot 拦截器详解

✅ 引言

在 Spring Boot 开发中,拦截器(Interceptor) 是一个强大而灵活的工具,常用于:

  • ✅ 用户登录状态校验
  • ✅ 接口权限控制
  • ✅ 请求日志记录
  • ✅ 接口性能监控
  • ✅ 全局异常处理前预处理

它就像一道“关卡”,在请求到达控制器之前、之后,甚至视图渲染完成后,都可以进行干预和处理。

本文将带你深入理解 Spring Boot 拦截器的工作原理,结合代码示例生产实践,助你掌握这一核心技能。


📌 一、拦截器是什么?它和过滤器有什么区别?

1.1 拦截器(Interceptor) vs 过滤器(Filter)

特性拦截器(Interceptor)过滤器(Filter)
所属框架Spring MVCServlet
拦截范围只拦截 Controller 请求拦截所有 Web 请求(包括静态资源)
依赖容器依赖 Spring 容器,可注入 Bean不依赖 Spring,无法直接使用 @Autowired
执行时机在 DispatcherServlet 调用 Handler 时触发在请求进入 Servlet 容器时触发

📌 简单说

  • Filter 是“大门守卫”,所有进出的都要检查。
  • Interceptor 是“办公室门卫”,只管进入办公区(Controller)的人。

📌 二、拦截器的生命周期(三阶段)

Spring Boot 拦截器有三个核心方法,对应请求处理的三个阶段:

public class MyInterceptor implements HandlerInterceptor {// 请求到达 Controller 前调用@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle: 请求预处理");// 返回 true:放行,继续执行// 返回 false:中断,不再执行后续操作return true;}//Controller 方法执行后,视图渲染前调用@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle: 后处理(可修改ModelAndView)");}//整个请求完成后调用(包括视图渲染)@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("🎉 afterCompletion: 请求完成(可用于资源清理)");}
}

🔄 执行流程图

在这里插入图片描述


📌 三、实战:手把手实现一个登录拦截器

3.1 需求描述

  • 所有 /api/** 接口需要登录才能访问。
  • 登录接口 /api/login 放行。
  • 未登录用户访问受保护接口时,返回 401 状态码。

3.2 实现步骤

第一步:创建拦截器
@Component
public class LoginInterceptor implements HandlerInterceptor {private static final String LOGIN_URI = "/api/login";private static final String TOKEN_HEADER = "Authorization";@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 放行登录接口if (LOGIN_URI.equals(request.getRequestURI())) {return true;}// 2. 检查请求头中的 tokenString token = request.getHeader(TOKEN_HEADER);if (token == null || token.isEmpty()) {response.setStatus(401);response.getWriter().write("{\"code\":401,\"msg\":\"未登录\"}");return false;}// 3. 校验 token(简化版:只判断非空)// 实际项目中应调用 AuthService 校验 JWT 或查询 Redisif (!"valid-token-123".equals(token)) {response.setStatus(401);response.getWriter().write("{\"code\":401,\"msg\":\"token无效\"}");return false;}// 4. 登录校验通过,放行return true;}
}
第二步:注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/api/**")        // 拦截所有 /api 开头的请求.excludePathPatterns("/api/login", "/api/public/**"); // 排除登录和公共接口}
}
第三步:测试 Controller
@RestController
@RequestMapping("/api")
public class TestController {@GetMapping("/login")public String login() {return "{\"token\":\"valid-token-123\"}";}@GetMapping("/user/info")public String userInfo() {return "{\"id\":1, \"name\":\"张三\"}";}@GetMapping("/admin/dashboard")public String admin() {return "{\"data\":\"admin only\"}";}
}

✅ 测试结果

请求Header[Authorization]结果
GET /api/login-✅ 返回 token
GET /api/user/infovalid-token-123✅ 返回用户信息
GET /api/user/info(空)❌ 401 未登录
GET /api/admin/dashboardinvalid-token❌ 401 token无效

📌 四、高级用法:使用拦截器记录接口性能

@Component
public class PerformanceInterceptor implements HandlerInterceptor {private static final Logger log = LoggerFactory.getLogger(PerformanceInterceptor.class);// 使用 ThreadLocal 存储开始时间private static final ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>();@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {long startTime = System.currentTimeMillis();startTimeThreadLocal.set(startTime);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {long startTime = startTimeThreadLocal.get();long endTime = System.currentTimeMillis();long duration = endTime - startTime;// 记录耗时超过 100ms 的慢请求if (duration > 100) {log.warn("⚠️ 慢请求: {} {} | 耗时: {}ms | Status: {}", request.getMethod(), request.getRequestURI(), duration,response.getStatus());}// 清理 ThreadLocal,防止内存泄漏startTimeThreadLocal.remove();}
}

📌 注意:使用 ThreadLocal 后,必须在 afterCompletion 中调用 remove(),否则可能导致内存泄漏。


📌 五、拦截器的局限性与替代方案

5.1 拦截器无法拦截的场景

  • 异步请求(@Async
  • WebSocket 请求
  • 文件上传的 Multipart 解析阶段

5.2 替代方案:使用 AOP(面向切面编程)

对于更复杂的逻辑(如参数校验、缓存、重试),推荐使用 Spring AOP

@Aspect
@Component
public class ServiceLogAspect {@Around("@annotation(com.example.annotation.LogExecution)")public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();Object result = joinPoint.proceed();long duration = System.currentTimeMillis() - start;System.out.println("方法 " + joinPoint.getSignature() + " 执行耗时: " + duration + "ms");return result;}
}

📌 六、生产环境最佳实践

实践说明
✅ 使用 @Component + @Configuration 注册避免硬编码
✅ 合理设置 addPathPatternsexcludePathPatterns避免误拦截静态资源
preHandle 中避免耗时操作否则会拖慢所有请求
ThreadLocal 用完必须 remove()防止内存泄漏
✅ 拦截器中不要抛异常应通过 response 返回错误码
✅ 多个拦截器注意顺序先注册的先执行 preHandle,后执行 afterCompletion

✅ 总结

方法适用场景
preHandle权限校验、日志记录、性能监控起点
postHandle修改 Model、添加 Header
afterCompletion资源清理、性能监控终点、异常处理

💡 一句话总结
拦截器是 Spring Boot 中控制请求流程的“闸门”,掌握它,你就掌握了 Web 请求的“调度权”。


📚 推荐

  • Spring 官方文档 - Interceptors
http://www.lryc.cn/news/621998.html

相关文章:

  • HarmonyOS Camera Kit 全解析:从基础拍摄到跨设备协同的实战指南
  • 开源 Arkts 鸿蒙应用 开发(十六)自定义绘图控件--波形图
  • 成品电池综合测试仪:一站式评估性能与安全
  • Flutter 以模块化方案 适配 HarmonyOS 的实现方法
  • 嵌入式学习日记(29)进程、线程
  • 一分钟了解EtherCAT 分支器
  • Web攻防-大模型应用LLM搭建接入第三方内容喂养AI插件安全WiKI库技术赋能
  • Linux操作系统从入门到实战(二十三)详细讲解进程虚拟地址空间
  • 【数据可视化-90】2023 年城镇居民人均收入可视化分析:Python + pyecharts打造炫酷暗黑主题大屏
  • Redis 知识点与应用场景
  • Web 开发 15
  • webrtc编译arm/arm64
  • C# 中的 string / StringBuilder / 值类型 / 引用类型 / CLR 总结
  • KNN算法:从电影分类到鸢尾花识别
  • 标准电子邮件地址格式(RFC 5322 里的 mailbox 语法)
  • 机器学习之PCA降维
  • 大模型系列——从训练到推理:网页数据在大语言模型中的新角色
  • Autosar之CanNm模块
  • ScanNet项目介绍
  • Rust 入门 泛型和特征-深入特征 (十五)
  • 从“写代码”到“定义需求”:AI编程工具如何重构软件开发的核心流程?
  • 【Mysql语句练习】
  • PCA降维全解析:从原理到实战
  • epoll发数据学习
  • Flink中的处理函数
  • 【完整源码+数据集+部署教程】小鼠行为识别系统源码和数据集:改进yolo11-RFAConv
  • JavaScript 原型机制详解:从概念到实战(附个人学习方法)
  • Flink中基于时间的合流--双流联结(join)
  • Java集合Map与Stream流:Map实现类特点、遍历方式、Stream流操作及Collections工具类方法
  • Transformer实战(11)——从零开始构建GPT模型