一、拦截器概述与核心概念
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. 拦截器执行流程
拦截器的完整执行顺序如下:
- 请求到达DispatcherServlet
- 拦截器1.preHandle() → 拦截器2.preHandle() → ... → 拦截器N.preHandle()
- 目标Controller方法执行
- 拦截器N.postHandle() ← ... ← 拦截器2.postHandle() ← 拦截器1.postHandle()
- 视图渲染
- 拦截器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
时注意及时清理,防止内存泄漏
六、常见问题与解决方案
-
拦截器不生效
- 检查是否添加了
@Configuration
注解 - 确认拦截路径配置正确
- 确保拦截器类被Spring管理(如添加
@Component
注解)
- 检查是否添加了
-
静态资源被拦截
- 使用
excludePathPatterns()
排除静态资源路径
- 使用
-
多个拦截器执行顺序问题
- 通过
order()
方法明确指定执行顺序
- 通过
-
获取请求Body内容
- 使用
ContentCachingRequestWrapper
包装请求,解决流只能读取一次的问题
- 使用
七、总结
Spring Boot拦截器是Web开发中强大的工具,通过合理使用可以:
- 集中处理公共逻辑,减少代码冗余
- 提高代码可维护性和可读性
- 实现安全控制、日志记录等横切关注点
在实际项目中,应根据具体需求选择合适的拦截策略,并注意性能优化和资源管理。结合AOP等其他技术,可以构建更加灵活、强大的请求处理管道。
通过本文的讲解和实战案例,开发者应能够掌握Spring Boot拦截器的核心概念、使用方法和最佳实践,从而在实际项目中灵活应用。