拦截器(Interceptor)是Spring MVC中非常重要的组件,它可以在请求处理的不同阶段进行拦截和处理。在Spring Boot中使用拦截器非常简单,下面我将详细介绍完整的使用方法和最佳实践。
一、拦截器基础概念
1. 拦截器 vs 过滤器
特性 | 拦截器(Interceptor) | 过滤器(Filter) |
---|---|---|
作用范围 | Spring MVC框架内 | Servlet容器层面 |
依赖 | 依赖Spring容器 | 不依赖Spring容器 |
实现方式 | 实现HandlerInterceptor接口 | 实现javax.servlet.Filter接口 |
获取信息能力 | 可以获取处理器方法信息、ModelAndView等 | 只能获取基本的Servlet API信息 |
2. 拦截器执行时机
二、创建自定义拦截器
1. 基本实现步骤
@Component
public class CustomInterceptor implements HandlerInterceptor {
// 在控制器方法执行前调用
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("preHandle方法执行");
// 可以进行权限验证、日志记录等
return true; // 返回true继续执行,false则中断
}
// 在控制器方法执行后,视图渲染前调用
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle方法执行");
}
// 在整个请求完成后调用(视图已渲染完成)
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
System.out.println("afterCompletion方法执行");
}
}
2. 注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private CustomInterceptor customInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customInterceptor)
.addPathPatterns("/**") // 拦截所有路径
.excludePathPatterns("/login"); // 排除登录路径
// 可以添加多个拦截器,按添加顺序执行
registry.addInterceptor(new AnotherInterceptor())
.addPathPatterns("/admin/**"); // 只拦截admin路径
}
}
三、拦截器高级用法
1. 多拦截器执行顺序
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 拦截器1 - 最先执行preHandle,最后执行afterCompletion
registry.addInterceptor(new Interceptor1())
.order(1);
// 拦截器2 - 后执行preHandle,先执行afterCompletion
registry.addInterceptor(new Interceptor2())
.order(2);
}
}
执行顺序:
preHandle 1 → preHandle 2 → controller → postHandle 2 → postHandle 1 → afterCompletion 2 → afterCompletion 1
2. 获取请求参数和处理器信息
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 获取请求参数
String param = request.getParameter("param");
// 判断handler类型
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 获取控制器类和方法信息
Class<?> controllerClass = handlerMethod.getBeanType();
Method method = handlerMethod.getMethod();
// 获取方法上的注解
GetMapping getMapping = method.getAnnotation(GetMapping.class);
}
return true;
}
3. 拦截器中注入Service
@Component
public class AuthInterceptor implements HandlerInterceptor {
// 可以直接注入Spring管理的Bean
@Autowired
private UserService userService;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (!userService.validateToken(token)) {
response.sendError(HttpStatus.UNAUTHORIZED.value(), "无效token");
return false;
}
return true;
}
}
四、实战案例
1. 登录认证拦截器
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
HttpSession session = request.getSession();
Object user = session.getAttribute("user");
if (user == null) {
// 未登录,重定向到登录页
response.sendRedirect("/login");
return false;
}
return true;
}
}
2. 接口耗时统计拦截器
@Component
public class TimeCostInterceptor implements HandlerInterceptor {
private static final ThreadLocal<Long> timeHolder = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
timeHolder.set(System.currentTimeMillis());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
long startTime = timeHolder.get();
long cost = System.currentTimeMillis() - startTime;
System.out.println("请求耗时: " + cost + "ms");
timeHolder.remove();
}
}
3. 全局异常处理拦截器
@Component
public class ExceptionInterceptor implements HandlerInterceptor {
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
if (ex != null) {
// 记录异常日志
log.error("请求处理异常", ex);
// 统一异常处理
response.setContentType("application/json");
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.getWriter().write("{\"code\":500,\"message\":\"服务异常\"}");
}
}
}
五、常见问题解决方案
1. 拦截器不生效问题排查
- 检查是否注册:确认在配置类中调用了
addInterceptor
- 检查路径匹配:确认
addPathPatterns
和excludePathPatterns
配置正确 - 检查顺序问题:其他拦截器可能中断了请求传递
- 检查Spring Boot版本:不同版本可能有差异
2. 静态资源被拦截问题
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/static/**", "/resources/**");
}
3. 拦截器中获取RequestBody
由于请求体只能读取一次,需要在过滤器中先缓存:
public class ContentCachingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
filterChain.doFilter(wrappedRequest, response);
}
}
然后在拦截器中:
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if (request instanceof ContentCachingRequestWrapper) {
ContentCachingRequestWrapper wrappedRequest = (ContentCachingRequestWrapper) request;
byte[] body = wrappedRequest.getContentAsByteArray();
String requestBody = new String(body, StandardCharsets.UTF_8);
// 使用requestBody
}
return true;
}
六、最佳实践建议
- 职责单一:每个拦截器只关注一个功能点
- 性能考虑:避免在拦截器中做耗时操作
- 合理排序:认证拦截器应该放在最前面
- 异常处理:注意拦截器中的异常处理逻辑
- 避免循环:拦截器逻辑不应导致循环重定向
七、总结
Spring Boot中的拦截器是一个非常强大的工具,合理使用可以:
- 实现统一的权限控制
- 记录请求日志
- 统计接口耗时
- 全局异常处理
- 参数预处理等
通过本文的介绍,你应该已经掌握了Spring Boot拦截器的完整使用方法。实际项目中可以根据需求灵活组合多个拦截器,构建强大的请求处理管道。