springboot 拦截器(超详细)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观
前言
在开发 Web 应用时,我们常常需要在请求到达控制器(Controller)之前或响应返回客户端之后,执行一些通用逻辑,例如登录验证、日志记录、性能监控或权限控制。此时,Spring Boot 拦截器(Interceptor)便成为了一个不可或缺的工具。它如同一座桥梁,允许开发者在请求处理的各个阶段插入自定义逻辑,从而提升代码的复用性和系统的可维护性。
本文将从拦截器的基础概念出发,结合实际案例和代码示例,深入讲解其工作原理、实现步骤及应用场景。无论是编程新手还是有一定经验的开发者,都能从中找到适合自己的学习路径。
一、拦截器是什么?它与过滤器有何不同?
1. 拦截器的定义
Spring Boot 拦截器是 Spring 框架提供的、基于 Java 的 AOP(面向切面编程)机制实现的一种请求处理组件。它允许开发者在请求被路由到具体的控制器方法之前(或之后)执行自定义逻辑。
形象比喻:
可以将拦截器想象成一座写字楼的“安检门”。当用户(请求)进入大楼(应用)时,安检门会检查其携带的物品(请求头、参数等),并根据规则决定是否放行。如果用户携带了违禁品(例如未登录用户尝试访问敏感接口),安检门会直接拦截,而无需进入办公区域(控制器方法)。
2. 拦截器与过滤器的区别
在 Web 开发中,拦截器和过滤器(Filter)都用于处理请求的预处理或后处理,但它们的工作阶段和适用场景不同:
对比维度 | 拦截器(Interceptor) | 过滤器(Filter) |
---|---|---|
工作阶段 | 基于 Spring MVC 的处理流程,在控制器方法调用前/后执行 | 基于 Servlet 规范,在更底层的 HTTP 请求处理阶段执行 |
功能范围 | 仅对 Spring MVC 处理的请求生效 | 对所有通过 Servlet 容器的请求生效 |
编程语言 | 纯 Java 代码实现,依赖 Spring 框架 | 可基于 Java 或其他语言(如 Groovy)实现 |
适用场景 | 需要与 Spring 的业务逻辑深度集成的场景 | 需要跨框架或非 Spring 控制的通用请求处理 |
总结:
如果需求仅涉及 Spring MVC 的请求处理流程,优先选择拦截器;若需要处理非 Spring 控制的请求(如静态资源、非 Spring 管理的接口),则使用过滤器更合适。
二、拦截器的核心原理与执行流程
1. 拦截器的生命周期
一个典型的拦截器需要实现 HandlerInterceptor
接口,并重写以下三个方法:
preHandle()
:在控制器方法执行之前调用,决定是否放行请求。postHandle()
:在控制器方法执行之后、视图渲染之前调用,可用于后置处理(如记录日志)。afterCompletion()
:在整个请求处理流程完成后调用,通常用于资源释放或最终记录。
2. 执行流程图解
客户端请求 → 过滤器(Filter) → 拦截器(preHandle) → 控制器方法 → 拦截器(postHandle) → 视图渲染 → 拦截器(afterCompletion)
3. 拦截器链的顺序
当配置多个拦截器时,它们的执行顺序遵循以下规则:
preHandle()
方法按配置顺序依次执行;postHandle()
方法按逆序执行(与配置顺序相反);afterCompletion()
方法同样按逆序执行。
示例:
假设配置了拦截器 A 和拦截器 B,则执行顺序为:
preHandle(A) → preHandle(B) → 控制器方法 → postHandle(B) → postHandle(A) → afterCompletion(B) → afterCompletion(A)
三、如何实现一个简单的拦截器?
1. 步骤一:创建拦截器类
首先,创建一个实现 HandlerInterceptor
接口的类,并重写所需方法。
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class LoginInterceptor implements HandlerInterceptor {
// 在控制器方法执行前拦截请求
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 检查用户是否登录(例如从 Session 中获取用户信息)
Object user = request.getSession().getAttribute("user");
if (user == null) {
// 未登录,返回 401 状态码
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false; // 拦截请求,不继续执行
}
return true; // 放行请求
}
}
2. 步骤二:注册拦截器
通过实现 WebMvcConfigurer
接口,将拦截器添加到 Spring MVC 的拦截器链中。
import org.springframework.beans.factory.annotation.Autowired;
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 {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加拦截器,并指定拦截路径
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/api/**") // 拦截所有以 /api/ 开头的请求
.excludePathPatterns("/api/public/**"); // 排除 /api/public/** 的路径
}
}
3. 步骤三:测试拦截器
通过发送一个未登录的请求到 /api/user
,预期返回 401 Unauthorized
状态码:
curl -X GET http://localhost:8080/api/user
四、实战案例:拦截器的常见应用场景
1. 场景一:登录验证拦截器
需求:确保用户访问 /api/private
接口时已登录。
实现逻辑:
- 在
preHandle()
中检查Session
中是否存在用户信息; - 若未登录,直接返回
401
状态码并终止请求。
代码示例:
// 在 LoginInterceptor 类中实现
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
if (request.getRequestURI().contains("/api/private")) {
// 针对特定路径增强校验
// ...
}
return super.preHandle(request, response, handler);
}
2. 场景二:日志拦截器
需求:记录每个请求的耗时和响应状态码。
实现逻辑:
- 在
preHandle()
中记录请求开始时间; - 在
afterCompletion()
中计算耗时并输出日志。
public class LoggingInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
request.setAttribute("startTime", System.currentTimeMillis());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
long startTime = (Long) request.getAttribute("startTime");
long duration = System.currentTimeMillis() - startTime;
logger.info("Request: {} took {} ms, Status: {}",
request.getRequestURI(), duration, response.getStatus());
}
}
3. 场景三:权限控制拦截器
需求:根据用户角色限制对 /api/admin
接口的访问。
实现逻辑:
- 在
preHandle()
中检查用户角色是否为ADMIN
; - 若角色不符,返回
403 Forbidden
状态码。
public class RoleInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
String role = (String) request.getSession().getAttribute("role");
if (!"ADMIN".equals(role)) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return false;
}
return true;
}
}
五、高级技巧与常见问题
1. 拦截器链的顺序管理
通过调整 addInterceptors()
方法中拦截器的添加顺序,可以控制拦截器的执行优先级。例如:
registry.addInterceptor(interceptorA).addPathPatterns("/**");
registry.addInterceptor(interceptorB).addPathPatterns("/**");
// interceptorA 的 preHandle() 会先于 interceptorB 执行
2. 如何处理跨域请求?
拦截器本身不直接处理跨域问题,但可以通过以下方式结合使用:
- 在拦截器中设置响应头(如
Access-Control-Allow-Origin
); - 或者使用过滤器(Filter)统一处理跨域请求。
3. 拦截器与异常处理
若拦截器内部抛出异常,可以通过 Spring 的全局异常处理器(@ControllerAdvice
)捕获并处理,避免直接暴露技术细节。
4. 性能优化建议
- 避免在拦截器中执行耗时操作(如复杂的数据库查询),以免影响请求响应速度;
- 使用缓存或静态配置优化拦截逻辑(如路径匹配规则)。
六、总结与展望
通过本文的学习,我们掌握了 Spring Boot 拦截器的核心概念、实现方法及典型应用场景。拦截器不仅简化了重复代码的编写,还为系统提供了灵活的扩展能力。无论是登录验证、日志记录还是权限控制,拦截器都能帮助开发者高效实现这些需求。
随着项目规模的扩大,合理设计拦截器链和管理拦截逻辑的优先级将成为关键。未来,结合 Spring AOP 或其他框架(如 Feign Client 的拦截器),拦截器的使用场景将进一步扩展。希望读者能通过实践,将拦截器的威力充分融入到自己的项目中!
通过本文的学习,读者可以快速掌握 Spring Boot 拦截器的开发与应用技巧,为构建更健壮、可维护的 Web 应用奠定坚实基础。