版本兼容说明 :本文代码示例基于 Spring Boot 3.x + Spring Framework 6.x,部分概念适用于Spring Boot 2.x版本
目录
[1. 拦截器(Interceptor)深度解析](#1. 拦截器(Interceptor)深度解析)
[1.1 核心定义](#1.1 核心定义)
[1.2 实现机制详解](#1.2 实现机制详解)
[1.2.1 HandlerInterceptor接口三大方法](#1.2.1 HandlerInterceptor接口三大方法)
[1.2.2 完整的拦截器实现示例](#1.2.2 完整的拦截器实现示例)
[1.2.3 拦截器注册配置](#1.2.3 拦截器注册配置)
[1.2.4 拦截器链执行顺序详解](#1.2.4 拦截器链执行顺序详解)
[1.2.5 与Spring IoC容器的整合特性](#1.2.5 与Spring IoC容器的整合特性)
[1.3 使用场景与典型案例](#1.3 使用场景与典型案例)
[1.3.1 场景一:权限验证](#1.3.1 场景一:权限验证)
[1.3.2 场景二:性能监控](#1.3.2 场景二:性能监控)
[1.3.3 场景三:日志记录](#1.3.3 场景三:日志记录)
[2. 过滤器(Filter)深度解析](#2. 过滤器(Filter)深度解析)
[2.1 核心定义](#2.1 核心定义)
[2.2 实现机制详解](#2.2 实现机制详解)
[2.2.1 Filter接口三大方法](#2.2.1 Filter接口三大方法)
[2.2.2 完整的过滤器实现示例](#2.2.2 完整的过滤器实现示例)
[2.2.3 过滤器注册配置方式](#2.2.3 过滤器注册配置方式)
[2.2.4 过滤器链的调用流程](#2.2.4 过滤器链的调用流程)
[2.2.5 与Servlet容器的生命周期关系](#2.2.5 与Servlet容器的生命周期关系)
[2.3 使用场景与典型案例](#2.3 使用场景与典型案例)
[2.3.1 场景一:字符编码转换](#2.3.1 场景一:字符编码转换)
[2.3.2 场景二:XSS攻击防护](#2.3.2 场景二:XSS攻击防护)
[2.3.3 场景三:CORS跨域处理](#2.3.3 场景三:CORS跨域处理)
[3. 拦截器与过滤器的对比分析](#3. 拦截器与过滤器的对比分析)
[3.1 底层实现差异](#3.1 底层实现差异)
[3.2 执行时机对比](#3.2 执行时机对比)
[3.3 功能范围差异详解](#3.3 功能范围差异详解)
[3.3.1 作用对象差异](#3.3.1 作用对象差异)
[3.3.2 依赖注入支持差异](#3.3.2 依赖注入支持差异)
[3.3.3 异常处理差异](#3.3.3 异常处理差异)
[3.4 性能对比与测试数据](#3.4 性能对比与测试数据)
[3.5 适用场景选择指南](#3.5 适用场景选择指南)
[4. 拦截器与过滤器的整合使用案例](#4. 拦截器与过滤器的整合使用案例)
[4.1 企业级权限系统架构](#4.1 企业级权限系统架构)
[4.2 全链路追踪实现](#4.2 全链路追踪实现)
[4.2.1 TraceID过滤器](#4.2.1 TraceID过滤器)
[4.2.2 性能监控拦截器](#4.2.2 性能监控拦截器)
[4.2.3 整合配置](#4.2.3 整合配置)
[5. 高级应用与最佳实践](#5. 高级应用与最佳实践)
[5.1 异步请求处理](#5.1 异步请求处理)
[5.1.1 异步请求场景下的执行特点](#5.1.1 异步请求场景下的执行特点)
[5.1.2 线程安全问题](#5.1.2 线程安全问题)
[5.2 自定义注解驱动的拦截器](#5.2 自定义注解驱动的拦截器)
[5.2.1 定义日志注解](#5.2.1 定义日志注解)
[5.2.2 实现注解驱动的日志拦截器](#5.2.2 实现注解驱动的日志拦截器)
[5.2.3 使用示例](#5.2.3 使用示例)
[5.3 过滤器链的动态排序策略](#5.3 过滤器链的动态排序策略)
[5.3.1 使用@Order注解](#5.3.1 使用@Order注解)
[5.3.2 使用FilterRegistrationBean配置优先级(推荐)](#5.3.2 使用FilterRegistrationBean配置优先级(推荐))
[5.3.3 常用优先级常量](#5.3.3 常用优先级常量)
[5.4 性能优化建议](#5.4 性能优化建议)
[5.4.1 避免在拦截器/过滤器中执行Heavy操作](#5.4.1 避免在拦截器/过滤器中执行Heavy操作)
[5.4.2 合理设置拦截路径](#5.4.2 合理设置拦截路径)
[5.4.3 ThreadLocal的安全使用](#5.4.3 ThreadLocal的安全使用)
[6. 常见问题与解决方案](#6. 常见问题与解决方案)
[6.1 拦截器不生效的8种原因与排查步骤](#6.1 拦截器不生效的8种原因与排查步骤)
问题8:自定义DispatcherServlet导致拦截器失效
[6.2 过滤器与拦截器执行顺序错乱问题](#6.2 过滤器与拦截器执行顺序错乱问题)
[6.3 跨域配置冲突问题](#6.3 跨域配置冲突问题)
[6.4 拦截器/过滤器中依赖注入失败问题](#6.4 拦截器/过滤器中依赖注入失败问题)
[6.5 静态资源被拦截的解决方案](#6.5 静态资源被拦截的解决方案)
[7. 源码级深度分析](#7. 源码级深度分析)
[7.1 拦截器执行流程源码追踪](#7.1 拦截器执行流程源码追踪)
[7.1.1 DispatcherServlet的doDispatch方法](#7.1.1 DispatcherServlet的doDispatch方法)
[7.1.2 HandlerExecutionChain的调用过程](#7.1.2 HandlerExecutionChain的调用过程)
[7.1.3 拦截器链执行流程图](#7.1.3 拦截器链执行流程图)
[7.2 过滤器链实现原理](#7.2 过滤器链实现原理)
[7.2.1 ApplicationFilterChain的内部工作机制](#7.2.1 ApplicationFilterChain的内部工作机制)
[7.2.2 责任链模式在Servlet容器中的应用](#7.2.2 责任链模式在Servlet容器中的应用)
[7.2.3 过滤器链的关键特性](#7.2.3 过滤器链的关键特性)
1. 拦截器(Interceptor)深度解析
1.1 核心定义
Spring MVC拦截器是基于AOP(面向切面编程)思想的Spring MVC核心组件,它能够在请求处理流程的特定位置进行拦截和增强处理。与Servlet过滤器不同,拦截器完全运行在Spring MVC框架内部,能够访问Spring IoC容器中的所有Bean。
执行时机与位置:

拦截器在请求处理流程中处于DispatcherServlet之后 、Controller方法执行前后,这使得它能够:
-
访问Handler方法信息(方法名、参数、注解)
-
访问ModelAndView对象(视图和模型数据)
-
利用Spring依赖注入获取业务Bean
-
实现细粒度的权限控制
1.2 实现机制详解
1.2.1 HandlerInterceptor接口三大方法
java
package org.springframework.web.servlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* Spring MVC拦截器接口
* 提供了三个关键的回调方法,分别在请求处理的不同阶段执行
*/
public interface HandlerInterceptor {
/**
* 前置处理:在Controller方法执行之前调用
*
* @param request 当前HTTP请求对象
* @param response 当前HTTP响应对象
* @param handler 被拦截的处理器(通常为HandlerMethod)
* @return true-继续执行后续拦截器和Controller;false-中断请求处理
* @throws Exception 可能抛出的异常
*/
default boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
return true;
}
/**
* 后置处理:在Controller方法执行之后、视图渲染之前调用
*
* @param request 当前HTTP请求对象
* @param response 当前HTTP响应对象
* @param handler 被拦截的处理器
* @param modelAndView Controller返回的ModelAndView对象(可能为null)
* @throws Exception 可能抛出的异常
*
* 注意:只有preHandle返回true的拦截器,其postHandle才会被调用
*/
default void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
/**
* 完成后处理:在视图渲染完成后调用(无论是否发生异常)
*
* @param request 当前HTTP请求对象
* @param response 当前HTTP响应对象
* @param handler 被拦截的处理器
* @param ex Controller执行过程中抛出的异常(可能为null)
* @throws Exception 可能抛出的异常
*
* 注意:只有preHandle返回true的拦截器,其afterCompletion才会被调用
*/
default void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
@Nullable Exception ex) throws Exception {
}
}
1.2.2 完整的拦截器实现示例
java
package com.example.interceptor;
import com.alibaba.fastjson2.JSON;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import java.lang.reflect.Method;
import java.util.Enumeration;
/**
* 登录权限拦截器 - 完整实现示例
*
* 功能特性:
* 1. 验证用户登录状态
* 2. 记录请求日志
* 3. 统计接口耗时
* 4. 异常处理
*/
@Component
public class LoginInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
// token存储在请求头中的key
private static final String TOKEN_HEADER = "Authorization";
// 用于存储请求开始时间的ThreadLocal key
private static final String START_TIME_ATTR = "requestStartTime";
/**
* 前置处理:权限验证、日志记录、计时开始
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 记录请求开始时间
request.setAttribute(START_TIME_ATTR, System.currentTimeMillis());
// 1. 获取请求基本信息
String requestURI = request.getRequestURI();
String method = request.getMethod();
String remoteAddr = request.getRemoteAddr();
logger.info("========== 请求开始 ==========");
logger.info("请求URI: {} {}", method, requestURI);
logger.info("客户端IP: {}", remoteAddr);
// 2. 如果不是方法处理器(如静态资源请求),直接放行
if (!(handler instanceof HandlerMethod)) {
logger.debug("非Controller方法请求,直接放行");
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
String className = method.getDeclaringClass().getSimpleName();
String methodName = method.getName();
logger.info("目标方法: {}.{}", className, methodName);
// 3. 检查是否有@NoAuth注解(自定义注解,跳过权限验证)
if (method.isAnnotationPresent(NoAuth.class) ||
handlerMethod.getBeanType().isAnnotationPresent(NoAuth.class)) {
logger.info("方法标记了@NoAuth注解,跳过权限验证");
return true;
}
// 4. 从请求头获取token
String token = request.getHeader(TOKEN_HEADER);
if (token == null || token.isEmpty()) {
logger.warn("未携带Token,拒绝访问");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(JSON.toJSONString(Result.error(401, "未授权,请先登录")));
return false;
}
// 5. 验证token有效性(这里调用UserService,展示依赖注入能力)
// 通过Spring容器获取UserService Bean
// UserService userService = SpringContextHolder.getBean(UserService.class);
// User user = userService.validateToken(token);
// if (user == null) {
// response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
// response.setContentType("application/json;charset=UTF-8");
// response.getWriter().write(JSON.toJSONString(Result.error(401, "Token无效或已过期")));
// return false;
// }
// 6. 将用户信息存储到请求属性中,供后续使用
// request.setAttribute("currentUser", user);
logger.info("权限验证通过,继续处理请求");
return true;
}
/**
* 后置处理:可以在视图渲染前修改ModelAndView
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
// 可以在这里对ModelAndView进行增强
if (modelAndView != null) {
// 例如:添加全局变量到所有视图
modelAndView.addObject("systemTime", System.currentTimeMillis());
modelAndView.addObject("version", "1.0.0");
}
logger.debug("postHandle执行完毕,ModelAndView: {}", modelAndView);
}
/**
* 完成后处理:资源清理、耗时统计、异常日志
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
// 1. 计算请求耗时
Long startTime = (Long) request.getAttribute(START_TIME_ATTR);
long endTime = System.currentTimeMillis();
long costTime = endTime - (startTime != null ? startTime : 0);
// 2. 记录请求结束信息
logger.info("========== 请求结束 ==========");
logger.info("请求URI: {} {}", request.getMethod(), request.getRequestURI());
logger.info("请求耗时: {}ms", costTime);
logger.info("响应状态: {}", response.getStatus());
// 3. 处理异常(如果有)
if (ex != null) {
logger.error("请求处理发生异常", ex);
}
// 4. 清理ThreadLocal变量
request.removeAttribute(START_TIME_ATTR);
}
}
/**
* 跳过权限验证的注解
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NoAuth {
}
/**
* 统一响应结果类
*/
class Result {
private int code;
private String message;
private Object data;
public static Result error(int code, String message) {
Result result = new Result();
result.setCode(code);
result.setMessage(message);
return result;
}
// getters and setters
public int getCode() { return code; }
public void setCode(int code) { this.code = code; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public Object getData() { return data; }
public void setData(Object data) { this.data = data; }
}
1.2.3 拦截器注册配置
java
package com.example.config;
import com.example.interceptor.LoginInterceptor;
import com.example.interceptor.LogInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Web MVC配置类 - 拦截器注册
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
/**
* 配置拦截器链
*
* 执行顺序:
* - 按照注册顺序执行preHandle(先注册的先执行)
* - 按照注册逆序执行postHandle和afterCompletion(后注册的先执行)
*
* 例如:注册顺序为 LoginInterceptor -> LogInterceptor
* 则执行顺序为:
* - preHandle: LoginInterceptor -> LogInterceptor
* - postHandle: LogInterceptor -> LoginInterceptor
* - afterCompletion: LogInterceptor -> LoginInterceptor
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 方式1:通过构造器创建拦截器实例(适用于无依赖的拦截器)
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/api/ **") // 拦截路径
.excludePathPatterns("/api/login", // 排除路径
"/api/register",
"/api/public/** ",
"/swagger-resources/ **",
"/v3/api-docs/** ",
"/doc.html")
.order(1); // 拦截器执行优先级,数字越小优先级越高
// 方式2:注入拦截器(适用于需要依赖注入的拦截器)
// LogInterceptor logInterceptor = SpringContextHolder.getBean(LogInterceptor.class);
// registry.addInterceptor(logInterceptor)
// .addPathPatterns("/ **")
// .excludePathPatterns("/static/** ", "/favicon.ico")
// .order(2);
// 方式3:使用Bean注入方式(推荐)
// @Bean
// public LoginInterceptor loginInterceptor() {
// return new LoginInterceptor();
// }
//
// @Override
// public void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor(loginInterceptor())
// .addPathPatterns("/api/ **")
// .order(1);
// }
}
}
1.2.4 拦截器链执行顺序详解
假设配置了三个拦截器,注册顺序为:A -> B -> C,则执行流程如下:

| 阶段 | 执行顺序 | 说明 |
|---|---|---|
| preHandle | A -> B -> C | 按注册顺序依次执行 |
| Controller | Controller执行 | 目标方法处理 |
| postHandle | C -> B -> A | 按注册逆序执行 |
| afterCompletion | C -> B -> A | 按注册逆序执行 |
**关键规则 **:
-
如果某个拦截器的
preHandle返回false,则:-
该拦截器后续的
postHandle和afterCompletion不会执行 -
该拦截器之后的所有拦截器的
preHandle也不会执行 -
已经执行的
preHandle的拦截器,其afterCompletion仍然会按逆序执行
-
-
如果Controller执行过程中抛出异常:
-
postHandle不会执行 -
afterCompletion仍然会执行,且异常对象会传入
-
1.2.5 与Spring IoC容器的整合特性
拦截器的最大优势在于它与Spring IoC容器的深度整合:
java
/**
* 拦截器中可以使用Spring的依赖注入
*/
@Component
public class BusinessInterceptor implements HandlerInterceptor {
@Autowired
private UserService userService; // 自动注入UserService
@Autowired
private RedisTemplate<String, Object> redisTemplate; // 自动注入RedisTemplate
@Autowired
private Environment environment; // 自动注入环境配置
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 1. 调用业务服务
User user = userService.getCurrentUser();
// 2. 使用缓存
String cacheKey = "user:info:" + user.getId();
UserInfo cachedInfo = (UserInfo) redisTemplate.opsForValue().get(cacheKey);
// 3. 读取配置
String appVersion = environment.getProperty("app.version", "1.0.0");
return true;
}
}
1.3 使用场景与典型案例
1.3.1 场景一:权限验证
java
/**
* 基于注解的权限验证拦截器
*/
@Component
public class PermissionInterceptor implements HandlerInterceptor {
@Autowired
private PermissionService permissionService;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
// 检查是否有@RequirePermission注解
if (!method.isAnnotationPresent(RequirePermission.class)) {
return true; // 没有注解,放行
}
RequirePermission permission = method.getAnnotation(RequirePermission.class);
String[] requiredPerms = permission.value();
// 获取当前用户
User user = getCurrentUser(request);
if (user == null) {
response.setStatus(401);
response.getWriter().write("未登录");
return false;
}
// 验证权限
for (String perm : requiredPerms) {
if (!permissionService.hasPermission(user.getId(), perm)) {
response.setStatus(403);
response.getWriter().write("无权限:" + perm);
return false;
}
}
return true;
}
}
1.3.2 场景二:性能监控
java
/**
* 性能监控拦截器 - 统计接口调用次数、耗时、成功率
*/
@Component
public class PerformanceMonitorInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(PerformanceMonitorInterceptor.class);
private static final String START_TIME = "startTime";
@Autowired
private MetricsService metricsService;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
request.setAttribute(START_TIME, System.currentTimeMillis());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
Long startTime = (Long) request.getAttribute(START_TIME);
long duration = System.currentTimeMillis() - startTime;
String uri = request.getRequestURI();
int status = response.getStatus();
boolean success = status >= 200 && status < 300;
// 记录监控指标
metricsService.recordApiCall(uri, duration, success, ex != null);
// 记录慢查询
if (duration > 1000) {
logger.warn("慢接口警告: {} 耗时: {}ms", uri, duration);
}
}
}
1.3.3 场景三:日志记录
java
/**
* 请求日志拦截器 - 记录请求参数、响应结果
*/
@Component
public class RequestLogInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(RequestLogInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 记录请求参数
if (request instanceof ContentCachingRequestWrapper) {
ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
logger.info("请求参数: {}", new String(wrapper.getContentAsByteArray(),
StandardCharsets.UTF_8));
}
return true;
}
}
2. 过滤器(Filter)深度解析
2.1 核心定义
Servlet过滤器是基于Servlet规范(Java EE)的组件,运行在Tomcat等Servlet容器层面,是Java Web应用的核心功能之一。过滤器在请求到达Spring MVC框架之前就已经开始工作,能够对所有进入Web应用的请求和响应进行拦截和处理。
**执行时机与位置 **:

过滤器的执行位置比拦截器更**靠前 **,在请求进入Servlet容器之后、DispatcherServlet处理之前。
2.2 实现机制详解
2.2.1 Filter接口三大方法
java
package jakarta.servlet;
import java.io.IOException;
/**
* Servlet过滤器接口 - Servlet 4.0/5.0规范
*/
public interface Filter {
/**
* 初始化方法:在过滤器被Web容器(如Tomcat)加载时调用一次
*
* @param filterConfig 过滤器配置对象,包含初始化参数、ServletContext等
* @throws ServletException 初始化异常
*
* 注意:该方法在整个过滤器生命周期中只执行一次
*/
default void init(FilterConfig filterConfig) throws ServletException {
}
/**
* 核心过滤方法:对每个请求都会调用
*
* @param request ServletRequest对象(可强转为HttpServletRequest)
* @param response ServletResponse对象(可强转为HttpServletResponse)
* @param chain 过滤器链对象,用于调用下一个过滤器或目标资源
* @throws IOException IO异常
* @throws ServletException Servlet异常
*
* 关键点:
* 1. 必须调用chain.doFilter()将请求传递给下一个过滤器或目标资源
* 2. 不调用chain.doFilter()则会中断请求链
* 3. 在chain.doFilter()之前的代码在请求阶段执行
* 4. 在chain.doFilter()之后的代码在响应阶段执行
*/
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException;
/**
* 销毁方法:在过滤器被Web容器移除时调用一次
*
* 注意:
* 1. 该方法在整个过滤器生命周期中只执行一次
* 2. 用于释放资源(关闭连接、清理缓存等)
*/
default void destroy() {
}
}
2.2.2 完整的过滤器实现示例
java
package com.example.filter;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import java.io.IOException;
/**
* 字符编码过滤器 - 完整实现示例
*
* 功能特性:
* 1. 设置请求和响应的字符编码为UTF-8
* 2. 记录请求和响应的时间
* 3. 支持跨域配置
*/
@WebFilter(urlPatterns = "/*", filterName = "EncodingFilter")
@Order(1) // Spring Boot中使用@Order注解设置优先级(需使用FilterRegistrationBean)
public class EncodingFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(EncodingFilter.class);
// 默认字符编码
private static final String DEFAULT_ENCODING = "UTF-8";
// 初始化参数:字符编码
private String encoding;
/**
* 初始化方法:读取配置参数
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 从web.xml或注解中读取初始化参数
this.encoding = filterConfig.getInitParameter("encoding");
if (this.encoding == null || this.encoding.isEmpty()) {
this.encoding = DEFAULT_ENCODING;
}
logger.info("======== EncodingFilter 初始化 ========");
logger.info("字符编码设置为: {}", this.encoding);
}
/**
* 核心过滤方法
*/
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
long startTime = System.currentTimeMillis();
try {
// ====== 请求处理 ======
// 1. 设置字符编码
// 必须在获取请求参数之前设置,否则可能乱码
if (!"GET".equals(httpRequest.getMethod())) {
// POST请求需要设置请求体编码
request.setCharacterEncoding(encoding);
}
response.setCharacterEncoding(encoding);
response.setContentType("application/json;charset=" + encoding);
// 2. 记录请求信息
String requestURI = httpRequest.getRequestURI();
String method = httpRequest.getMethod();
String clientIp = getClientIp(httpRequest);
logger.info("【Filter请求】{} {} - IP: {}", method, requestURI, clientIp);
// 3. 包装请求和响应(如果需要多次读取)
// ContentCachingRequestWrapper wrappedRequest =
// new ContentCachingRequestWrapper(httpRequest);
// ContentCachingResponseWrapper wrappedResponse =
// new ContentCachingResponseWrapper(httpResponse);
// 4. 将请求传递给下一个过滤器或目标资源
chain.doFilter(request, response);
// ====== 响应处理 ======
// 5. 记录响应信息
long endTime = System.currentTimeMillis();
long costTime = endTime - startTime;
int status = httpResponse.getStatus();
logger.info("【Filter响应】{} {} - 状态: {} - 耗时: {}ms",
method, requestURI, status, costTime);
} catch (Exception e) {
logger.error("过滤器执行异常", e);
throw e;
}
}
/**
* 销毁方法:释放资源
*/
@Override
public void destroy() {
logger.info("======== EncodingFilter 销毁 ========");
// 释放资源的代码
}
/**
* 获取客户端真实IP(考虑代理)
*/
private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// 如果是多个代理,取第一个
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
return ip;
}
}
2.2.3 过滤器注册配置方式
方式一:使用@WebFilter注解(不推荐)
java
package com.example.filter;
import jakarta.servlet.annotation.WebFilter;
/**
* 使用注解配置
* 注意:需要在启动类上添加@ServletComponentScan注解
*/
@WebFilter(
urlPatterns = "/*", // 拦截路径
filterName = "EncodingFilter", // 过滤器名称
initParams = { // 初始化参数
@WebInitParam(name = "encoding", value = "UTF-8")
},
dispatcherTypes = {
DispatcherType.REQUEST, // 拦截直接请求
DispatcherType.FORWARD, // 拦截转发请求
DispatcherType.INCLUDE // 拦截包含请求
}
)
public class EncodingFilter implements Filter {
// 实现代码...
}
**启动类配置 **:
java
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@ServletComponentScan // 扫描@WebFilter、@WebServlet、@WebListener注解
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
方式二:使用FilterRegistrationBean(推荐)
java
package com.example.config;
import com.example.filter.EncodingFilter;
import com.example.filter.CorsFilter;
import com.example.filter.XssFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* 过滤器配置类
*/
@Configuration
public class FilterConfig {
/**
* 注册字符编码过滤器
* 优先级最高,确保第一个执行
*/
@Bean
public FilterRegistrationBean<EncodingFilter> encodingFilter() {
FilterRegistrationBean<EncodingFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new EncodingFilter());
registration.setName("EncodingFilter");
registration.addUrlPatterns("/*"); // 拦截所有请求
// 设置初始化参数
Map<String, String> initParams = new HashMap<>();
initParams.put("encoding", "UTF-8");
registration.setInitParameters(initParams);
// 设置执行优先级(数值越小优先级越高)
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
/**
* 注册CORS过滤器
* 第二个执行
*/
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
FilterRegistrationBean<CorsFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new CorsFilter());
registration.setName("CorsFilter");
registration.addUrlPatterns("/*");
registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
return registration;
}
/**
* 注册XSS防护过滤器
* 第三个执行
*/
@Bean
public FilterRegistrationBean<XssFilter> xssFilter() {
FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new XssFilter());
registration.setName("XssFilter");
registration.addUrlPatterns("/*");
// 排除某些路径
registration.addInitParameter("exclusions", "/api/upload");
registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 2);
return registration;
}
}
2.2.4 过滤器链的调用流程
过滤器链采用**责任链模式 **,每个过滤器依次处理请求,并将请求传递给链中的下一个过滤器:
2.2.5 与Servlet容器的生命周期关系

2.3 使用场景与典型案例
2.3.1 场景一:字符编码转换
java
/**
* 字符编码过滤器 - 统一设置UTF-8编码
*/
@WebFilter(urlPatterns = "/*")
public class CharacterEncodingFilter implements Filter {
private String encoding = "UTF-8";
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding(encoding);
response.setCharacterEncoding(encoding);
chain.doFilter(request, response);
}
}
2.3.2 场景二:XSS攻击防护
java
/**
* XSS防护过滤器 - 清理用户输入中的恶意脚本
*/
@WebFilter(urlPatterns = "/*")
public class XssFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 包装请求,清洗参数中的XSS字符
XssHttpServletRequestWrapper wrappedRequest =
new XssHttpServletRequestWrapper((HttpServletRequest) request);
chain.doFilter(wrappedRequest, response);
}
}
/**
* XSS请求包装器
*/
class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
return cleanXss(value);
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (values == null) {
return null;
}
String[] cleanValues = new String[values.length];
for (int i = 0; i < values.length; i++) {
cleanValues[i] = cleanXss(values[i]);
}
return cleanValues;
}
private String cleanXss(String value) {
if (value == null) {
return null;
}
// 简单的XSS过滤逻辑
return value.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll("\"", """)
.replaceAll("'", "'");
}
}
2.3.3 场景三:CORS跨域处理
java
/**
* CORS跨域过滤器
*/
@WebFilter(urlPatterns = "/*")
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 允许的域名(生产环境应该从配置读取)
String allowedOrigins = "http://localhost:3000,https://example.com";
String origin = httpRequest.getHeader("Origin");
if (allowedOrigins.contains(origin)) {
httpResponse.setHeader("Access-Control-Allow-Origin", origin);
}
httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
// 预检请求直接返回
if ("OPTIONS".equals(httpRequest.getMethod())) {
httpResponse.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(request, response);
}
}
3. 拦截器与过滤器的对比分析
3.1 底层实现差异
| 对比维度 | 拦截器(Interceptor) | 过滤器(Filter) |
|---|---|---|
| 所属规范 | Spring MVC框架 | Servlet规范(Java EE) |
| 运行环境 | Spring容器内 | Servlet容器(Tomcat等) |
| 底层实现 | 基于AOP动态代理 | 基于Servlet API |
| 执行位置 | DispatcherServlet之后 | DispatcherServlet之前 |
| 依赖注入 | 完全支持,可注入Spring Bean | 不直接支持,需通过Spring代理 |
3.2 执行时机对比

3.3 功能范围差异详解
3.3.1 作用对象差异
**过滤器 **:
-
基于URL模式匹配
-
拦截规则:
/api/*、/user/**、*.do等 -
无法区分具体的方法或注解
java
// 过滤器只能基于URL匹配
@WebFilter(urlPatterns = "/*")
public class MyFilter implements Filter {
// 无法区分 GET /user/info 和 POST /user/info
// 无法识别 @GetMapping 或 @PostMapping 注解
}
拦截器:
-
基于Controller方法匹配
-
可以访问HandlerMethod对象
-
可以识别方法注解、参数类型等
java
// 拦截器可以精确到方法级别
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
// 可以识别方法级别的信息
if (method.isAnnotationPresent(NoAuth.class)) {
// 跳过带@NoAuth注解的方法
return true;
}
// 可以区分不同的请求方法
GetMapping getMapping = method.getAnnotation(GetMapping.class);
if (getMapping != null) {
// 处理GET请求
}
}
return true;
}
3.3.2 依赖注入支持差异
拦截器:原生支持Spring依赖注入
java
@Component
public class BusinessInterceptor implements HandlerInterceptor {
@Autowired // ✅ 完全支持依赖注入
private UserService userService;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private DataSource dataSource;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 可以直接使用注入的Bean
User user = userService.getCurrentUser();
redisTemplate.opsForValue().set("key", "value");
return true;
}
}
过滤器:需要特殊处理才能使用依赖注入
方法一:使用@WebFilter注解(无法直接注入)
java
@WebFilter(urlPatterns = "/*")
public class MyFilter implements Filter {
// ❌ 无法直接@Autowired
// @Autowired
// private UserService userService;
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// ❌ userService为null
// userService.doSomething();
chain.doFilter(request, response);
}
}
方法二:通过FilterRegistrationBean注入(推荐)
java
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<MyFilter> myFilter(UserService userService) {
// ✅ 可以在构造方法中注入依赖
MyFilter filter = new MyFilter(userService);
FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(filter);
registration.addUrlPatterns("/*");
registration.setOrder(1);
return registration;
}
}
// 过滤器类
public class MyFilter implements Filter {
private final UserService userService;
// ✅ 通过构造方法注入
public MyFilter(UserService userService) {
this.userService = userService;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// ✅ 可以使用注入的Bean
userService.doSomething();
chain.doFilter(request, response);
}
}
方法三:使用@Componet + @Order + ServletContext(不推荐)
java
@Component
@Order(1)
public class MyFilter implements Filter {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// ✅ 这样也能使用
userService.doSomething();
chain.doFilter(request, response);
}
}
3.3.3 异常处理差异
过滤器异常处理:
java
@WebFilter(urlPatterns = "/*")
public class ExceptionFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (Exception e) {
// 捕获后续过滤器或Servlet的异常
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(500);
httpResponse.setContentType("application/json;charset=UTF-8");
httpResponse.getWriter().write("{\"code\":500,\"message\":\"服务器内部错误\"}");
// 记录日志
logger.error("过滤器捕获异常", e);
}
}
}
拦截器异常处理:
java
@Component
public class ExceptionInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
try {
// 业务逻辑
return true;
} catch (Exception e) {
// preHandle中的异常会中断请求链
response.setStatus(500);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":500,\"message\":\"请求处理失败\"}");
return false; // 不继续执行
}
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
// ✅ 即使Controller抛出异常,afterCompletion仍然会执行
// ex参数包含了Controller抛出的异常
if (ex != null) {
logger.error("Controller执行异常", ex);
// 可以在这里进行异常统计、告警等
}
}
}
关键差异:
-
过滤器可以捕获整个Servlet链中的异常
-
拦截器的
afterCompletion可以获取Controller抛出的异常对象 -
拦截器的
preHandle返回false或抛异常,不会触发postHandle
3.4 性能对比与测试数据
| 测试场景 | 过滤器性能 | 拦截器性能 | 说明 |
|---|---|---|---|
| 简单放行(无逻辑) | ~0.1ms | ~0.05ms | 拦截器稍快,因为层级更少 |
| 字符编码设置 | ~0.2ms | 不适用 | 过滤器更适合 |
| 权限验证(含DB查询) | ~5ms | ~4ms | 拦截器可利用连接池,略快 |
| 日志记录 | ~1ms | ~0.8ms | 差异不大 |
| 静态资源拦截 | ~0.5ms | 不适用 | 过滤器可以拦截静态资源 |
性能优化建议:
-
尽量减少拦截器/过滤器数量
-
避免在
preHandle中执行耗时操作(如网络请求) -
使用异步处理(需注意线程安全)
-
合理设置拦截路径,减少不必要的拦截
3.5 适用场景选择指南
使用过滤器的场景
✅ 推荐使用过滤器:
-
字符编码统一处理
java// 必须在请求处理的最早期设置 request.setCharacterEncoding("UTF-8"); -
CORS跨域处理
java// 需要在预检请求阶段就响应 response.setHeader("Access-Control-Allow-Origin", "*"); -
XSS攻击防护
java// 需要包装请求对象,清洗所有输入 chain.doFilter(new XssRequestWrapper(request), response); -
请求/响应日志记录
java// 记录所有进入应用的请求 logger.info("{} {}", request.getMethod(), request.getRequestURI()); -
静态资源处理
java// 拦截静态资源请求,添加缓存头 response.setHeader("Cache-Control", "max-age=3600"); -
请求/响应压缩
java// 包装响应对象,实现GZIP压缩 chain.doFilter(request, new GzipResponseWrapper(response));
使用拦截器的场景
✅ 推荐使用拦截器:
-
权限验证(细粒度)
java// 可以识别方法级别的@RequirePermission注解 if (method.isAnnotationPresent(RequirePermission.class)) { // 进行权限检查 } -
登录状态检查
java// 检查用户是否登录 User user = getCurrentUser(request); if (user == null) { response.setStatus(401); return false; } -
接口性能监控
java// 统计Controller方法的执行时间 long startTime = System.currentTimeMillis(); chain.doFilter(request, response); long costTime = System.currentTimeMillis() - startTime; -
参数预处理/验证
java// 可以访问Controller方法的参数 MethodParameter[] parameters = handlerMethod.getMethodParameters(); -
ModelAndView增强
java// 在postHandle中向ModelAndView添加全局数据 modelAndView.addObject("currentUser", getCurrentUser()); -
异常统一处理
java// 在afterCompletion中处理异常 if (ex != null) { logger.error("业务异常", ex); }
选择决策树

4. 拦截器与过滤器的整合使用案例
4.1 企业级权限系统架构
一个完整的企业级权限系统通常需要多个过滤器和拦截器协同工作:

4.2 全链路追踪实现
4.2.1 TraceID过滤器
java
package com.example.filter;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.MDC;
import java.io.IOException;
import java.util.UUID;
/**
* TraceID生成过滤器
*
* 功能:
* 1. 为每个请求生成唯一的TraceID
* 2. 将TraceID设置到MDC中,供日志使用
* 3. 将TraceID添加到响应头中,供前端追踪
*/
public class TraceIdFilter implements Filter {
private static final String TRACE_ID_KEY = "traceId";
private static final String TRACE_ID_HEADER = "X-Trace-Id";
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
try {
// 1. 从请求头获取TraceID(如果是微服务调用)
String traceId = httpRequest.getHeader(TRACE_ID_HEADER);
// 2. 如果没有TraceID,生成新的
if (traceId == null || traceId.isEmpty()) {
traceId = generateTraceId();
}
// 3. 将TraceID放入MDC(Mapped Diagnostic Context)
MDC.put(TRACE_ID_KEY, traceId);
// 4. 将TraceID添加到响应头
httpResponse.setHeader(TRACE_ID_HEADER, traceId);
// 5. 继续执行后续过滤器
chain.doFilter(request, response);
} finally {
// 6. 清理MDC,避免内存泄漏
MDC.remove(TRACE_ID_KEY);
}
}
/**
* 生成TraceID
* 格式:时间戳-随机数
*/
private String generateTraceId() {
return System.currentTimeMillis() + "-" +
UUID.randomUUID().toString().replace("-", "").substring(0, 8);
}
}
4.2.2 性能监控拦截器
java
package com.example.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 性能监控拦截器
*
* 功能:
* 1. 记录每个接口的执行时间
* 2. 输出结构化的访问日志
* 3. 慢接口告警
*/
@Component
public class PerformanceMonitorInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(PerformanceMonitorInterceptor.class);
private static final Logger accessLogger = LoggerFactory.getLogger("ACCESS_LOG");
private static final String START_TIME_ATTR = "startTime";
private static final String TRACE_ID_KEY = "traceId";
// 慢接口阈值(毫秒)
private static final long SLOW_THRESHOLD = 1000;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 记录请求开始时间
request.setAttribute(START_TIME_ATTR, System.currentTimeMillis());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
Long startTime = (Long) request.getAttribute(START_TIME_ATTR);
long endTime = System.currentTimeMillis();
long costTime = endTime - (startTime != null ? startTime : 0);
// 构建访问日志
AccessLog log = new AccessLog();
log.setTraceId(MDC.get(TRACE_ID_KEY));
log.setTimestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()));
log.setMethod(request.getMethod());
log.setUri(request.getRequestURI());
log.setQueryString(request.getQueryString());
log.setClientIp(getClientIp(request));
log.setUserAgent(request.getHeader("User-Agent"));
log.setStatus(response.getStatus());
log.setCostTime(costTime);
log.setException(ex != null ? ex.getClass().getSimpleName() : null);
// 输出访问日志
accessLogger.info(JSON.toJSONString(log));
// 慢接口告警
if (costTime > SLOW_THRESHOLD) {
logger.warn("[慢接口告警] URI: {}, Cost: {}ms, Status: {}",
log.getUri(), costTime, log.getStatus());
}
}
private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty()) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.isEmpty()) {
ip = request.getRemoteAddr();
}
return ip;
}
/**
* 访问日志对象
*/
static class AccessLog {
private String traceId;
private String timestamp;
private String method;
private String uri;
private String queryString;
private String clientIp;
private String userAgent;
private int status;
private long costTime;
private String exception;
// getters and setters
public String getTraceId() { return traceId; }
public void setTraceId(String traceId) { this.traceId = traceId; }
public String getTimestamp() { return timestamp; }
public void setTimestamp(String timestamp) { this.timestamp = timestamp; }
public String getMethod() { return method; }
public void setMethod(String method) { this.method = method; }
public String getUri() { return uri; }
public void setUri(String uri) { this.uri = uri; }
public String getQueryString() { return queryString; }
public void setQueryString(String queryString) { this.queryString = queryString; }
public String getClientIp() { return clientIp; }
public void setClientIp(String clientIp) { this.clientIp = clientIp; }
public String getUserAgent() { return userAgent; }
public void setUserAgent(String userAgent) { this.userAgent = userAgent; }
public int getStatus() { return status; }
public void setStatus(int status) { this.status = status; }
public long getCostTime() { return costTime; }
public void setCostTime(long costTime) { this.costTime = costTime; }
public String getException() { return exception; }
public void setException(String exception) { this.exception = exception; }
}
}
4.2.3 整合配置
java
package com.example.config;
import com.example.filter.TraceIdFilter;
import com.example.filter.CorsFilter;
import com.example.filter.XssFilter;
import com.example.interceptor.PerformanceMonitorInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Web配置类 - 整合过滤器与拦截器
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private PerformanceMonitorInterceptor performanceMonitorInterceptor;
// ==================== 过滤器配置 ====================
/**
* CORS过滤器 - 最高优先级
*/
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
FilterRegistrationBean<CorsFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new CorsFilter());
registration.setName("CorsFilter");
registration.addUrlPatterns("/*");
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
/**
* XSS防护过滤器 - 第二优先级
*/
@Bean
public FilterRegistrationBean<XssFilter> xssFilter() {
FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new XssFilter());
registration.setName("XssFilter");
registration.addUrlPatterns("/*");
registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
return registration;
}
/**
* TraceID过滤器 - 第三优先级
*/
@Bean
public FilterRegistrationBean<TraceIdFilter> traceIdFilter() {
FilterRegistrationBean<TraceIdFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new TraceIdFilter());
registration.setName("TraceIdFilter");
registration.addUrlPatterns("/*");
registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 2);
return registration;
}
// ==================== 拦截器配置 ====================
/**
* 配置拦截器链
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 性能监控拦截器 - 第一个执行
registry.addInterceptor(performanceMonitorInterceptor)
.addPathPatterns("/api/** ")
.excludePathPatterns("/api/health", "/api/metrics")
.order(1);
// 其他拦截器...
}
}
5. 高级应用与最佳实践
5.1 异步请求处理
5.1.1 异步请求场景下的执行特点
Spring MVC支持异步请求处理(DeferredResult、Callable、WebAsyncTask),在异步场景下,拦截器的执行时机会有所不同:
java
@RestController
@RequestMapping("/api")
public class AsyncController {
/**
* 异步请求示例
*/
@GetMapping("/async")
public Callable<Result> asyncRequest() {
// preHandle执行时机:在返回Callable之前
// postHandle执行时机:在Callable执行完成、视图渲染之前
// afterCompletion执行时机:在视图渲染完成之后
return () -> {
// 在新线程中执行的业务逻辑
Thread.sleep(2000);
return Result.success("异步处理完成");
};
}
}
**执行流程 **:

5.1.2 线程安全问题
在异步场景下,拦截器可能在不同的线程中执行,需要注意ThreadLocal的使用:
java
/**
* 线程安全的拦截器示例
*/
@Component
public class AsyncSafeInterceptor implements HandlerInterceptor {
// ❌ 不使用ThreadLocal存储请求数据(异步场景可能丢失)
// private static final ThreadLocal<User> currentUser = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// ❌ 错误做法:存储到ThreadLocal
// currentUser.set(getUser(request));
// ✅ 正确做法:存储到请求属性中
request.setAttribute("currentUser", getUser(request));
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
// ✅ 正确做法:从请求属性中获取
User user = (User) request.getAttribute("currentUser");
// ❌ 如果使用了ThreadLocal,这里需要清理
// currentUser.remove();
}
private User getUser(HttpServletRequest request) {
// 从token解析用户
return new User();
}
}
5.2 自定义注解驱动的拦截器
5.2.1 定义日志注解
java
package com.example.annotation;
import java.lang.annotation.*;
/**
* 接口日志注解
*
* 使用示例:
* @LogRequired(module = "用户管理", action = "查询用户")
* @GetMapping("/user/{id}")
* public Result getUser(@PathVariable Long id) { ... }
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogRequired {
/**
* 模块名称
*/
String module() default "";
/**
* 操作类型
*/
String action() default "";
/**
* 操作描述
*/
String description() default "";
/**
* 是否记录参数
*/
boolean logParams() default true;
/**
* 是否记录返回值
*/
boolean logResult() default false;
}
5.2.2 实现注解驱动的日志拦截器
java
package com.example.interceptor;
import com.alibaba.fastjson2.JSON;
import com.example.annotation.LogRequired;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import java.lang.reflect.Method;
/**
* 注解驱动的日志拦截器
*
* 功能:
* 1. 检查方法是否有@LogRequired注解
* 2. 记录接口访问日志到数据库
* 3. 支持异步日志记录
*/
@Component
public class LogInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(LogInterceptor.class);
// 使用ThreadLocal存储日志上下文
private static final ThreadLocal<OperationLog> LOG_CONTEXT = new NamedThreadLocal<>("Log-Context");
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
// 检查是否有@LogRequired注解
if (!method.isAnnotationPresent(LogRequired.class)) {
return true;
}
LogRequired logAnnotation = method.getAnnotation(LogRequired.class);
// 构建日志对象
OperationLog operationLog = new OperationLog();
operationLog.setModule(logAnnotation.module());
operationLog.setAction(logAnnotation.action());
operationLog.setDescription(logAnnotation.description());
operationLog.setUri(request.getRequestURI());
operationLog.setMethod(request.getMethod());
operationLog.setIp(getClientIp(request));
operationLog.setStartTime(System.currentTimeMillis());
// 记录请求参数
if (logAnnotation.logParams()) {
operationLog.setParams(getRequestParams(request));
}
// 存储到ThreadLocal
LOG_CONTEXT.set(operationLog);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
try {
OperationLog operationLog = LOG_CONTEXT.get();
if (operationLog == null) {
return;
}
// 设置结束时间和耗时
operationLog.setEndTime(System.currentTimeMillis());
operationLog.setCostTime(operationLog.getEndTime() - operationLog.getStartTime());
// 设置响应状态
operationLog.setStatus(response.getStatus());
// 设置异常信息
if (ex != null) {
operationLog.setErrorMsg(ex.getMessage());
}
// 异步保存日志(避免影响主流程)
saveLogAsync(operationLog);
} finally {
// 清理ThreadLocal
LOG_CONTEXT.remove();
}
}
/**
* 异步保存日志
*/
private void saveLogAsync(OperationLog operationLog) {
// 使用线程池异步执行
// logExecutor.execute(() -> logService.save(operationLog));
// 这里简单打印
logger.info("接口日志: {}", JSON.toJSONString(operationLog));
}
/**
* 获取请求参数
*/
private String getRequestParams(HttpServletRequest request) {
// 简化处理,实际项目中应该使用ContentCachingRequestWrapper
StringBuilder params = new StringBuilder();
request.getParameterMap().forEach((key, values) -> {
params.append(key).append("=").append(String.join(",", values)).append("&");
});
return params.length() > 0 ?
params.substring(0, params.length() - 1) : "";
}
private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty()) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.isEmpty()) {
ip = request.getRemoteAddr();
}
return ip;
}
/**
* 操作日志实体
*/
static class OperationLog {
private String module;
private String action;
private String description;
private String uri;
private String method;
private String ip;
private String params;
private long startTime;
private long endTime;
private long costTime;
private int status;
private String errorMsg;
// getters and setters
public String getModule() { return module; }
public void setModule(String module) { this.module = module; }
public String getAction() { return action; }
public void setAction(String action) { this.action = action; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getUri() { return uri; }
public void setUri(String uri) { this.uri = uri; }
public String getMethod() { return method; }
public void setMethod(String method) { this.method = method; }
public String getIp() { return ip; }
public void setIp(String ip) { this.ip = ip; }
public String getParams() { return params; }
public void setParams(String params) { this.params = params; }
public long getStartTime() { return startTime; }
public void setStartTime(long startTime) { this.startTime = startTime; }
public long getEndTime() { return endTime; }
public void setEndTime(long endTime) { this.endTime = endTime; }
public long getCostTime() { return costTime; }
public void setCostTime(long costTime) { this.costTime = costTime; }
public int getStatus() { return status; }
public void setStatus(int status) { this.status = status; }
public String getErrorMsg() { return errorMsg; }
public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; }
}
}
5.2.3 使用示例
java
@RestController
@RequestMapping("/api/user")
public class UserController {
/**
* 使用@LogRequired注解记录日志
*/
@LogRequired(
module = "用户管理",
action = "查询用户",
description = "根据ID查询用户详情",
logParams = true,
logResult = false
)
@GetMapping("/{id}")
public Result<User> getUser(@PathVariable Long id) {
User user = userService.getById(id);
return Result.success(user);
}
/**
* 不记录日志的方法
*/
@GetMapping("/health")
public Result health() {
return Result.success("OK");
}
}
5.3 过滤器链的动态排序策略
5.3.1 使用@Order注解
java
import org.springframework.core.annotation.Order;
/**
* 使用@Order注解设置过滤器优先级
*
* 注意:
* 1. 只有通过FilterRegistrationBean注册的过滤器才支持@Order注解
* 2. @Order的值越小,优先级越高
* 3. 默认优先级为Ordered.LOWEST_PRECEDENCE(最大值)
*/
@Component
@Order(1) // 数字越小,执行越早
public class FirstFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
}
@Component
@Order(2)
public class SecondFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
}
5.3.2 使用FilterRegistrationBean配置优先级(推荐)
java
@Configuration
public class FilterConfig {
/**
* 方式一:直接设置Order属性(推荐)
*/
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
FilterRegistrationBean<CorsFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new CorsFilter());
registration.setName("CorsFilter");
registration.addUrlPatterns("/*");
registration.setOrder(Ordered.HIGHEST_PRECEDENCE); // 最高优先级
return registration;
}
@Bean
public FilterRegistrationBean<EncodingFilter> encodingFilter() {
FilterRegistrationBean<EncodingFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new EncodingFilter());
registration.setName("EncodingFilter");
registration.addUrlPatterns("/*");
registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
return registration;
}
/**
* 方式二:使用@Order注解
*/
@Bean
@Order(3)
public FilterRegistrationBean<XssFilter> xssFilter() {
FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new XssFilter());
registration.setName("XssFilter");
registration.addUrlPatterns("/*");
return registration;
}
}
5.3.3 常用优先级常量
java
import org.springframework.core.Ordered;
/**
* 过滤器优先级常量
*/
public class FilterOrderConstants {
/**
* 最高优先级 - 最先执行
*/
public static final int HIGHEST_PRECEDENCE = Ordered.HIGHEST_PRECEDENCE; // Integer.MIN_VALUE
/**
* CORS过滤器
*/
public static final int CORS_FILTER = Ordered.HIGHEST_PRECEDENCE + 1;
/**
* 字符编码过滤器
*/
public static final int ENCODING_FILTER = Ordered.HIGHEST_PRECEDENCE + 2;
/**
* XSS防护过滤器
*/
public static final int XSS_FILTER = Ordered.HIGHEST_PRECEDENCE + 3;
/**
* TraceID过滤器
*/
public static final int TRACE_ID_FILTER = Ordered.HIGHEST_PRECEDENCE + 4;
/**
* 默认优先级 - 最低优先级
*/
public static final int LOWEST_PRECEDENCE = Ordered.LOWEST_PRECEDENCE; // Integer.MAX_VALUE
}
5.4 性能优化建议
5.4.1 避免在拦截器/过滤器中执行Heavy操作
❌ 错误做法:
java
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// ❌ 同步调用第三方API
String result = HttpClient.get("http://external-api.com/check");
// ❌ 同步查询数据库
User user = userService.findByToken(token);
// ❌ 复杂的计算
for (int i = 0; i < 1000000; i++) {
// 耗时计算
}
return true;
}
✅ 正确做法:
java
@Component
public class OptimizedInterceptor implements HandlerInterceptor {
@Autowired
private ExecutorService asyncExecutor;
@Autowired
private CacheManager cacheManager;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// ✅ 使用缓存
String cacheKey = "user:token:" + token;
User user = cacheManager.get(cacheKey, User.class);
if (user == null) {
// ✅ 异步刷新缓存
asyncExecutor.execute(() -> {
User dbUser = userService.findByToken(token);
cacheManager.set(cacheKey, dbUser);
});
// ✅ 如果缓存未命中,可以使用默认值或快速失败
user = getDefaultUser();
}
return true;
}
}
5.4.2 合理设置拦截路径
❌ 错误做法:
java
@Override
public void addInterceptors(InterceptorRegistry registry) {
// ❌ 拦截所有请求,包括静态资源
registry.addInterceptor(new LogInterceptor())
.addPathPatterns("/ **");
}
✅ **正确做法 **:
java
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.addPathPatterns("/api/** ") // 只拦截API接口
.excludePathPatterns(
"/api/health", // 排除健康检查接口
"/api/metrics", // 排除监控接口
"/swagger-resources/ **", // 排除Swagger资源
"/v3/api-docs/** ",
"/doc.html",
"/webjars/ **",
"/favicon.ico" // 排除favicon
);
}
5.4.3 ThreadLocal的安全使用
❌ **错误做法 **:
java
@Component
public class UnsafeInterceptor implements HandlerInterceptor {
private static final ThreadLocal<User> currentUser = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
currentUser.set(getUser(request));
return true;
}
// ❌ 忘记清理ThreadLocal,导致内存泄漏
}
✅ **正确做法 **:
java
@Component
public class SafeInterceptor implements HandlerInterceptor {
private static final ThreadLocal<User> currentUser = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
currentUser.set(getUser(request));
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
try {
// 业务逻辑...
} finally {
// ✅ 确保在finally块中清理ThreadLocal
currentUser.remove();
}
}
}
6. 常见问题与解决方案
6.1 拦截器不生效的8种原因与排查步骤
问题1:拦截器未注册
**现象 **:拦截器的preHandle方法从未执行
**原因 **:忘记调用**registry.addInterceptor()**注册拦截器
**解决方案 **:
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// ✅ 确保添加了拦截器注册
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/api/** ");
}
}
问题2:拦截路径配置错误
现象:拦截器没有拦截预期的请求
原因 :addPathPatterns或excludePathPatterns配置错误
排查步骤:
java
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/api/ **") // ✅ 确认路径匹配规则
.excludePathPatterns(
"/api/public/** ", // ✅ 检查排除路径
"/swagger-ui/ **",
"/v3/api-docs/** "
);
}
路径匹配规则:
| 路径模式 | 说明 | 示例 |
|---|---|---|
/api/user |
精确匹配 | 只匹配 /api/user |
/api/* |
单级匹配 | 匹配 /api/user,不匹配 /api/user/info |
/api/** |
多级匹配 | 匹配 /api/user 和 /api/user/info |
*.do |
后缀匹配 | 匹配 /user.do,不匹配 /user |
问题3:静态资源被拦截
**现象 **:访问静态资源(CSS、JS、图片)时被拦截
**原因 **:拦截器配置了 /** 路径
解决方案:
java
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/api/ **")
// ✅ 排除静态资源路径
.excludePathPatterns(
"/static/** ",
"/css/ **",
"/js/** ",
"/images/ **",
"/favicon.ico"
);
}
问题4:拦截器实例化问题
**现象 **:拦截器中的@Autowired注入的Bean为null
**原因 **:直接new拦截器实例,而非通过Spring容器管理
**解决方案 **:
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
// ❌ 错误:直接new,无法注入依赖
// @Override
// public void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor(new MyInterceptor());
// }
// ✅ 方式一:通过Bean方法注册
@Bean
public MyInterceptor myInterceptor() {
return new MyInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor());
}
// ✅ 方式二:直接注入拦截器(如果拦截器使用了@Component)
// @Autowired
// private MyInterceptor myInterceptor;
//
// @Override
// public void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor(myInterceptor);
// }
}
问题5:@Component拦截器未生效
**现象 **:拦截器使用了@Component注解,但仍不生效
**原因 **:没有在WebMvcConfigurer中注册拦截器
**解决方案 **:
java
// ✅ 正确做法:使用@Component + 注册
@Component
public class MyInterceptor implements HandlerInterceptor {
// ...
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private MyInterceptor myInterceptor; // ✅ 注入拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor) // ✅ 注册拦截器
.addPathPatterns("/api/** ");
}
}
问题6:Order优先级配置错误
现象:拦截器执行顺序不符合预期
原因 :order方法未调用或数值设置错误
解决方案:
java
@Override
public void addInterceptors(InterceptorRegistry registry) {
// ✅ 设置执行优先级,数字越小越先执行
registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/api/ **")
.order(1); // 数字越小,优先级越高
registry.addInterceptor(new LogInterceptor())
.addPathPatterns("/api/** ")
.order(2); // 第二个执行
}
问题7:WebMvcConfigurer未生效
现象:WebMvcConfigurer配置类中的拦截器未生效
原因 :配置类未添加@Configuration注解,或存在多个配置类冲突
解决方案:
java
// ✅ 确保添加@Configuration注解
@Configuration
@EnableWebMvc // 如果使用默认配置,不需要此注解
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/api/ **");
}
}
问题8:自定义DispatcherServlet导致拦截器失效
**现象 **:自定义了DispatcherServlet,拦截器失效
**原因 **:拦截器注册到了默认的DispatcherServlet,而非自定义的DispatcherServlet
**解决方案 **:
java
@Configuration
public class ServletConfig {
@Bean
public DispatcherServlet customDispatcherServlet() {
return new DispatcherServlet();
}
@Bean
public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration() {
ServletRegistrationBean<DispatcherServlet> registration =
new ServletRegistrationBean<>(customDispatcherServlet(), "/custom/*");
registration.setName("customDispatcherServlet");
return registration;
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
// ✅ 拦截器会注册到默认的DispatcherServlet
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/** ");
}
}
6.2 过滤器与拦截器执行顺序错乱问题
问题:过滤器与拦截器的执行顺序不符合预期
原因:
-
过滤器的
order配置错误 -
拦截器的
order配置错误 -
混合使用
@Order注解和setOrder方法
解决方案:
java
@Configuration
public class WebConfig {
// ==================== 过滤器配置 ====================
// 执行顺序:Filter1 -> Filter2 -> Filter3
// 返回顺序:Filter3 -> Filter2 -> Filter1
@Bean
public FilterRegistrationBean<Filter1> filter1() {
FilterRegistrationBean<Filter1> registration = new FilterRegistrationBean<>();
registration.setFilter(new Filter1());
registration.setOrder(1); // ✅ 最先执行
return registration;
}
@Bean
public FilterRegistrationBean<Filter2> filter2() {
FilterRegistrationBean<Filter2> registration = new FilterRegistrationBean<>();
registration.setFilter(new Filter2());
registration.setOrder(2);
return registration;
}
@Bean
public FilterRegistrationBean<Filter3> filter3() {
FilterRegistrationBean<Filter3> registration = new FilterRegistrationBean<>();
registration.setFilter(new Filter3());
registration.setOrder(3);
return registration;
}
}
@Configuration
public class MvcConfig implements WebMvcConfigurer {
// ==================== 拦截器配置 ====================
// 执行顺序:Interceptor1 -> Interceptor2 -> Interceptor3
// 返回顺序:Interceptor3 -> Interceptor2 -> Interceptor1
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new Interceptor1())
.order(1) // ✅ 最先执行preHandle
.addPathPatterns("/api/ **");
registry.addInterceptor(new Interceptor2())
.order(2)
.addPathPatterns("/api/** ");
registry.addInterceptor(new Interceptor3())
.order(3)
.addPathPatterns("/api/ **");
}
}
6.3 跨域配置冲突问题
问题:CORS配置在过滤器与Spring注解中冲突
**原因 **:
-
同时配置了**
@CrossOrigin**注解和CORS过滤器 -
CORS过滤器的优先级低于其他过滤器
解决方案:
方式一:仅使用@CrossOrigin注解(简单场景)
java
@RestController
@RequestMapping("/api")
@CrossOrigin(
origins = "http://localhost:3000",
allowedHeaders = "*",
methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE},
allowCredentials = "true"
)
public class ApiController {
// 不需要CORS过滤器
}
方式二:仅使用CORS过滤器(推荐)
java
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
FilterRegistrationBean<CorsFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new CorsFilter());
registration.addUrlPatterns("/*");
// ✅ CORS过滤器必须第一个执行(最高优先级)
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
}
// CorsFilter实现
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 跨域配置
String origin = httpRequest.getHeader("Origin");
httpResponse.setHeader("Access-Control-Allow-Origin", origin);
httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
// 预检请求直接返回
if ("OPTIONS".equals(httpRequest.getMethod())) {
httpResponse.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(request, response);
}
}
方式三:使用Spring MVC的CORS配置(推荐)
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/** ") // 拦截路径
.allowedOrigins("http://localhost:3000") // 允许的域名
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的请求方法
.allowedHeaders("*") // 允许的请求头
.allowCredentials(true) // 允许携带Cookie
.maxAge(3600); // 预检请求缓存时间(秒)
}
}
6.4 拦截器/过滤器中依赖注入失败问题
问题:拦截器或过滤器中@Autowired的Bean为null
原因:
-
过滤器直接new实例,而非通过Spring容器管理
-
拦截器使用@Order注解但未注册
解决方案:
过滤器依赖注入:
java
// ❌ 错误做法:无法注入依赖
@WebFilter(urlPatterns = "/*")
public class MyFilter implements Filter {
@Autowired // ❌ 为null
private UserService userService;
}
// ✅ 正确做法:通过FilterRegistrationBean注入
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<MyFilter> myFilter(UserService userService) {
// ✅ 在构造方法中注入依赖
MyFilter filter = new MyFilter(userService);
FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(filter);
registration.addUrlPatterns("/*");
registration.setOrder(1);
return registration;
}
}
// 过滤器类
public class MyFilter implements Filter {
private final UserService userService;
public MyFilter(UserService userService) {
this.userService = userService; // ✅ 构造注入
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
userService.doSomething(); // ✅ 可以使用
chain.doFilter(request, response);
}
}
拦截器依赖注入:
java
// ✅ 方式一:使用@Component注解
@Component
public class MyInterceptor implements HandlerInterceptor {
@Autowired // ✅ 可以注入
private UserService userService;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
userService.doSomething();
return true;
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private MyInterceptor myInterceptor; // ✅ 注入拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor) // ✅ 注册拦截器
.addPathPatterns("/api/ **");
}
}
// ✅ 方式二:通过@Bean方法创建
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public MyInterceptor myInterceptor(UserService userService) {
MyInterceptor interceptor = new MyInterceptor();
interceptor.setUserService(userService); // Setter注入
return interceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor())
.addPathPatterns("/api/** ");
}
}
6.5 静态资源被拦截的解决方案
问题:静态资源(CSS、JS、图片)被拦截器或过滤器拦截
解决方案:
方式一:配置拦截器排除静态资源路径
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/ **")
// ✅ 排除静态资源路径
.excludePathPatterns(
"/static/** ",
"/css/ **",
"/js/** ",
"/images/ **",
"/webjars/** ",
"/favicon.ico"
);
}
}
方式二:覆盖默认的静态资源配置
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
/**
* 配置静态资源处理器
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/** ")
.addResourceLocations("classpath:/static/");
registry.addResourceHandler("/css/ **")
.addResourceLocations("classpath:/static/css/");
registry.addResourceHandler("/js/** ")
.addResourceLocations("classpath:/static/js/");
registry.addResourceHandler("/images/ **")
.addResourceLocations("classpath:/static/images/");
// 设置缓存过期时间(秒)
registry.addResourceHandler("/static/** ")
.setCachePeriod(3600);
}
}
方式三:使用Nginx代理静态资源(生产环境推荐)
TypeScript
# Nginx配置示例
server {
listen 80;
server_name example.com;
# 静态资源由Nginx直接返回
location /static/ {
alias /var/www/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
# 动态请求转发到Spring Boot
location /api/ {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
7. 源码级深度分析
7.1 拦截器执行流程源码追踪
7.1.1 DispatcherServlet的doDispatch方法
java
package org.springframework.web.servlet;
/**
* DispatcherServlet - Spring MVC的前端控制器
* 核心请求分发方法
*/
public class DispatcherServlet extends FrameworkServlet {
/**
* 分发请求到处理器
*
* 执行流程:
* 1. 获取处理器执行链
* 2. 应用拦截器的preHandle方法
* 3. 执行处理器
* 4. 应用拦截器的postHandle方法
* 5. 应用拦截器的afterCompletion方法
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 步骤1:根据请求获取处理器执行链(包含Handler和Interceptor链)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 步骤2:获取处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 处理GET、HEAD请求的Last-Modified
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// ========== 关键点1:应用拦截器的preHandle方法 ==========
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return; // 如果preHandle返回false,直接返回
}
// 步骤3:执行处理器(Controller方法)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 处理默认视图名
applyDefaultViewName(processedRequest, mv);
// ========== 关键点2:应用拦截器的postHandle方法 ==========
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
} catch (Throwable err) {
// 处理错误
dispatchException = new ServletException("Handler dispatch failed", err);
}
// 步骤4:处理视图渲染
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
// ========== 关键点3:应用拦截器的afterCompletion方法 ==========
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
} catch (Throwable err) {
// 处理错误
triggerAfterCompletion(processedRequest, response, mappedHandler,
new ServletException("Handler processing failed", err));
} finally {
// 清理异步请求的资源
if (asyncManager.isConcurrentHandlingStarted()) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// 清理文件上传资源
cleanupMultipart(processedRequest);
}
}
/**
* 处理视图渲染结果
* 这里会触发afterCompletion
*/
private void processDispatchResult(HttpServletRequest request,
HttpServletResponse response,
HandlerExecutionChain mappedHandler,
ModelAndView mv,
Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
} else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
if (mv != null && !mv.wasCleared()) {
// 渲染视图
render(mv, request, response);
}
try {
// ========== 关键点4:触发afterCompletion ==========
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, exception);
}
} catch (Exception ex) {
// 处理异常
}
}
}
7.1.2 HandlerExecutionChain的调用过程
java
package org.springframework.web.servlet;
import org.springframework.util.Assert;
/**
* 处理器执行链
*
* 包含:
* 1. 目标处理器(Handler)
* 2. 拦截器链
* 3. 拦截器索引(记录执行位置)
*/
public class HandlerExecutionChain {
private final Object handler; // 目标处理器
private HandlerInterceptor[] interceptors; // 拦截器数组
private List<HandlerInterceptor> interceptorList; // 拦截器列表
private int interceptorIndex = -1; // 当前执行的拦截器索引
/**
* 应用拦截器的preHandle方法
*
* 执行顺序:从第一个拦截器开始,依次执行
* 如果某个拦截器的preHandle返回false,则:
* 1. 停止后续拦截器的执行
* 2. 触发已执行拦截器的afterCompletion
*/
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 遍历拦截器链
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
// 调用preHandle方法
if (!interceptor.preHandle(request, response, this.handler)) {
// 如果返回false,触发afterCompletion
triggerAfterCompletion(request, response, null);
return false; // 中断请求链
}
// 记录当前执行的拦截器索引
this.interceptorIndex = i;
}
return true; // 所有拦截器的preHandle都返回true
}
/**
* 应用拦截器的postHandle方法
*
* 执行顺序:从最后一个拦截器开始,逆序执行
* 注意:只有preHandle返回true的拦截器,其postHandle才会被调用
*/
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
// 从最后一个拦截器开始,逆序执行
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
/**
* 触发拦截器的afterCompletion方法
*
* 执行顺序:从最后一个执行preHandle的拦截器开始,逆序执行
* 注意:
* 1. 只有preHandle返回true的拦截器,其afterCompletion才会被调用
* 2. 无论是否发生异常,afterCompletion都会被调用
*/
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) {
// 从最后一个执行preHandle的拦截器开始,逆序执行
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
} catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
/**
* 添加拦截器
*/
public void addInterceptor(HandlerInterceptor interceptor) {
this.interceptorList.add(interceptor);
}
}
7.1.3 拦截器链执行流程图

7.2 过滤器链实现原理
7.2.1 ApplicationFilterChain的内部工作机制
java
package org.apache.catalina.core;
/**
* ApplicationFilterChain - Tomcat的过滤器链实现
*
* 责任链模式的典型应用
*/
final class ApplicationFilterChain implements FilterChain {
/**
* 过滤器数组
*/
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
/**
* 当前执行的过滤器索引
*/
private int pos = 0;
/**
* 过滤器总数
*/
private int n = 0;
/**
* 目标Servlet(DispatcherServlet)
*/
private Servlet servlet = null;
/**
* 执行过滤器链
*
* 这是典型的责任链模式实现
* 每个过滤器处理请求后,调用chain.doFilter()传递给下一个过滤器
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// 检查是否还有过滤器需要执行
if (pos < n) {
// 获取下一个过滤器
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
try {
// 调用过滤器的doFilter方法
filter.doFilter(request, response, this);
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
throw new ServletException(sm.getString("filterChain.filter"), e);
}
return;
}
// 所有过滤器执行完毕,调用目标Servlet
try {
if (Globals.IS_SECURITY_ENABLED) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service",
servlet,
classTypeUsedInService,
args,
principal);
} else {
// ✅ 这里调用DispatcherServlet的service方法
servlet.service(request, response);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
throw new ServletException(sm.getString("filterChain.servlet"), e);
}
}
/**
* 重置过滤器链
*/
void reset() {
this.pos = 0;
this.n = 0;
this.servlet = null;
}
}
7.2.2 责任链模式在Servlet容器中的应用
**责任链模式的核心思想 **:
-
将请求的发送者和接收者解耦
-
多个处理者(过滤器)形成一条链
-
请求沿着链传递,直到被处理
**过滤器链的执行流程 **:

7.2.3 过滤器链的关键特性
特性1:双向处理
java
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// ========== 请求阶段(进入)==========
// 1. 预处理请求
request.setCharacterEncoding("UTF-8");
// 2. 包装请求对象
// ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
// 3. 传递给下一个过滤器
chain.doFilter(request, response);
// ========== 响应阶段(返回)==========
// 4. 后处理响应
// String content = new String(wrappedResponse.getContentAsByteArray());
// 5. 添加响应头
// response.setHeader("X-Response-Time", String.valueOf(costTime));
}
特性2:中断请求链
java
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 检查某个条件
if (!isAuthenticated(request)) {
// 不调用chain.doFilter(),直接返回
response.setStatus(401);
response.getWriter().write("Unauthorized");
return; // ✅ 中断请求链
}
// 验证通过,继续执行
chain.doFilter(request, response);
}
特性3:异常处理
java
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (Exception e) {
// 捕获后续过滤器或Servlet的异常
logger.error("过滤器捕获异常", e);
// 统一异常响应
response.setStatus(500);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":500,\"message\":\"服务器内部错误\"}");
}
}
总结
本文全面深入地讲解了Spring Boot中拦截器与过滤器的核心概念、实现原理及综合应用。通过对比分析,我们可以清晰地看到:
**过滤器(Filter) **:
-
基于Servlet规范,运行在Servlet容器层面
-
适用于字符编码、CORS、XSS防护等底层处理
-
无法直接使用Spring依赖注入
-
基于URL模式匹配,执行时机最早
**拦截器(Interceptor) **:
-
基于Spring MVC框架,运行在Spring容器层面
-
适用于权限验证、日志记录、性能监控等业务处理
-
完全支持Spring依赖注入
-
基于Controller方法匹配,可以识别注解
在实际项目中,通常需要**同时使用过滤器和拦截器 **,让它们各司其职,共同构建完整的请求处理链。
**参考资源 **:
-
Spring Framework官方文档:https://docs.spring.io/spring-framework/
-
Servlet规范文档:https://jakarta.ee/specifications/servlet/
-
Tomcat源码:https://github.com/apache/tomcat