在构建企业级 Web 应用时,我们经常需要处理登录校验、日志记录、权限验证等通用逻辑。如果把这些代码分散在每个接口中,不仅会导致大量重复,还会让代码变得难以维护。SpringBoot 拦截器正是解决这一痛点的利器——它能在请求到达 Controller 之前或之后,统一执行预定义的逻辑,让开发者专注于业务本身。本文将带你从零开始,深入理解拦截器的工作原理、核心方法以及最佳实践,并结合 Java、Python、TypeScript、Go、JavaScript 等语言中的类似概念进行对比,帮助你建立更全面的技术视野。
️ 拦截器到底是什么?一个生活场景帮你秒懂
想象你去银行办理业务:进门后不会直接冲到柜台,而是先取号、通过安检——这两步就是“拦截器”。如果带了身份证且取号成功,就放行去窗口(对应接口正常执行);如果没带身份证或取号失败,直接被拦下(请求被拒绝);业务办完后,你还可以给柜员评分,这相当于请求执行完毕后的后置处理。
在 SpringBoot 项目中,拦截器的角色完全一致。它位于请求链路中,在请求到达 Controller 接口 之前或之后,执行我们预先编写好的通用代码。无论是 Java 中的 Spring 拦截器,还是 Python 中的 Django 中间件,亦或是 TypeScript 和 JavaScript 中的 Express 中间件,其核心思想都是相通的:统一拦截、按需处理。使用拦截器后,你不需要在每个接口里重复写登录校验、日志记录等代码,大大减少了冗余,提升了可维护性。
⏱️ 拦截器的核心执行时机:三个关键方法
要实现一个拦截器,需要让自定义类实现 Spring 的 HandlerInterceptor 接口。该接口定义了三个核心方法,分别对应请求的不同生命周期阶段:
- preHandle(请求前):这是最常用的方法。请求到达后,首先执行此方法。返回
true表示放行,请求继续向 Controller 前进;返回false表示拦截,请求立即终止,后续的接口和其他拦截方法都不会执行。登录校验、权限验证等操作通常在此完成。 - postHandle(接口后):当
preHandle返回true且 Controller 接口执行完毕后,会执行此方法。你可以在这里对返回结果进行统一处理,比如添加公共响应头。 - afterCompletion(请求结束):这是拦截器的最后一个方法,在接口执行完毕且响应数据已经返回给前端后执行。通常用于资源清理,比如关闭流、释放连接,类似于 Go 中的
defer或 Python 中的finally块。
执行顺序总结如下:preHandle(请求前) → 执行 Controller 接口 → postHandle(接口后) → 返回响应 → afterCompletion(请求结束)
实战:用拦截器实现登录校验
1. 定义拦截器,编写校验逻辑
首先,创建一个拦截器类,实现 HandlerInterceptor 接口,并重写 preHandle 方法。核心逻辑是从 Session 或 Token 中获取用户信息:如果存在则放行,否则拦截并返回“未登录”提示。以下是一个典型实现:
@Component
@Slf4j // 日志注解,方便看执行过程
public class LoginInterceptor implements HandlerInterceptor {
// 核心:请求到达接口前做登录校验
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("preHandle目标方法执行之前");
// 获取Session,参数为false表示:如果没有Session,就不新建新得到Session对象
HttpSession session = request.getSession(false);
// 校验Session中是否有用户信息
if (session == null || session.getAttribute("loginUser") == null) {
log.warn("用户未登录");
// 设置响应编码,避免中文乱码
response.setContentType("text/html;charset=utf-8");
// 设置响应状态码401:未认证
response.setStatus(401);
// 给前端返回提示信息
response.getOutputStream().write("用户未登录,请先登录!".getBytes("UTF-8"));
return false; // 拦截请求
}
// 有用户信息,放行
log.info("用户已登录,放行请求");
return true;
}
}
实践建议:在实际项目中,推荐使用 JWT Token 而非 Session 进行校验,以支持分布式部署。此外,对于 TypeScript 或 JavaScript 开发者,可以类比 Express 中的中间件函数,其 next() 调用对应 Spring 中的 return true。
2. 注册配置拦截器,指定拦截路径
定义好拦截器后,Spring 并不知道要拦截哪些请求。你需要创建一个配置类,实现 WebMvcConfigurer 接口,并重写 addInterceptors 方法:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/book/**"); //这表示拦截/book目录之下的任意级路径
}
⚠️ 注意:登录接口、静态资源(如 CSS、JS、图片)通常需要排除拦截。如果使用 Go 的 Gin 框架,类似的配置通过中间件路由组实现;在 Python 的 Django 中,则通过 MIDDLEWARE 配置列表完成。

上图展示了路径配置的详细说明,包括通配符 /** 和 /* 的区别,以及如何精细控制拦截范围。
⚙️ 拦截器的高级用法与多语言对比
除了基础的登录校验,拦截器还能胜任更多场景:
- 接口耗时监控:在
preHandle中记录开始时间,在afterCompletion中计算并打印耗时。 - 统一日志记录:记录每个请求的 URL、参数、IP 等信息,便于问题排查。
- 跨域处理:在
preHandle中设置跨域响应头,实现 CORS 支持。 - 多拦截器链:可以注册多个拦截器,按顺序执行。例如先执行日志拦截器,再执行权限拦截器。
如果你熟悉其他语言,会发现类似机制无处不在:Java 的 Spring 拦截器、Python 的 Django 中间件、TypeScript 和 JavaScript 的 Express 中间件、Go 的 Gin 中间件——它们都遵循“洋葱模型”或“管道模型”,核心思想高度一致。掌握 SpringBoot 拦截器后,可以快速迁移到其他技术栈。
[AFFILIATE_SLOT_1]✅ 最佳实践与常见陷阱
在使用拦截器时,有几点需要特别注意:
- 避免在拦截器中执行耗时操作:拦截器位于请求主路径上,如果进行数据库查询或远程调用,会显著增加接口响应时间。建议只做轻量级校验,如 Token 解析。
- 正确区分拦截器与过滤器:过滤器(Filter)是 Servlet 容器层面的,可以拦截所有请求(包括静态资源);拦截器是 Spring 层面的,只能拦截进入 Spring 管理的请求。对于字符编码、安全防护等场景,优先使用过滤器。
- 注意异常处理:在
preHandle中返回false时,需要自行编写响应返回逻辑,否则前端会收到空响应。建议封装一个通用的响应工具类。 - 路径匹配规则:
/*只匹配一级路径(如/api/login),而/**匹配多级路径(如/api/v1/user)。配置时务必根据实际需求选择。
延伸思考:在微服务架构中,可以将拦截器中的校验逻辑提取为独立的鉴权服务,通过 TypeScript 或 JavaScript 的 API 网关统一处理,实现更灵活的解耦。
[AFFILIATE_SLOT_2]总结
SpringBoot 拦截器是处理通用请求逻辑的神器,核心在于“统一拦截、按需处理”。本文带你掌握了三个关键点:拦截器的作用(减少冗余代码)、三个核心方法(preHandle 最常用,返回 true 放行、false 拦截)、使用步骤(定义拦截器 + 注册配置)。无论你是 Java 开发者,还是涉猎 Python、TypeScript、Go、JavaScript 的全栈工程师,理解这一模式都能帮助你写出更优雅、可维护的代码。从今天起,让你的项目告别重复校验,用拦截器轻松管理请求生命周期吧!
浙公网安备 33010602011771号