一、核心概念:过滤器与拦截器的本质区别
1.1 技术定位对比
过滤器(Filter)
过滤器是 Servlet 规范的一部分,位于 Servlet 容器中,主要用于对 HTTP 请求和响应进行预处理和后处理。它工作在 Servlet 引擎层面,不依赖于 Spring 框架,可以在任何 Java Web 应用中使用。
拦截器(Interceptor)
拦截器是 Spring MVC 框架的组件,基于 AOP(面向切面编程)实现,用于在请求处理的不同阶段插入自定义逻辑。它工作在 Spring MVC 框架内部,与 Spring 容器深度集成。
1.2 调用流程差异
请求处理流程对比:
请求 -> 过滤器链 -> DispatcherServlet -> 拦截器链 -> 控制器 -> 拦截器链(后处理) -> 视图渲染 -> 过滤器链(响应处理)
关键区别点:
- 过滤器在 Servlet 容器层处理请求
- 拦截器在 Spring MVC 框架层处理请求
- 过滤器对所有请求生效
- 拦截器只对 Spring MVC 映射的请求生效
二、技术实现:从代码看差异
2.1 过滤器的实现
过滤器配置示例:
@WebFilter(urlPatterns = "/*", filterName = "customFilter")
public class CustomFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
// 初始化逻辑
System.out.println("过滤器初始化完成");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 请求预处理
HttpServletRequest httpRequest = (HttpServletRequest) request;
System.out.println("拦截请求: " + httpRequest.getRequestURI());
// 传递给下一个过滤器或Servlet
chain.doFilter(request, response);
// 响应后处理
System.out.println("响应处理完成");
}
@Override
public void destroy() {
// 销毁逻辑
System.out.println("过滤器销毁完成");
}
}
注册过滤器的另一种方式:
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean<CustomFilter> customFilter() {
FilterRegistrationBean<CustomFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new CustomFilter());
registration.addUrlPatterns("/*");
registration.setName("customFilter");
registration.setOrder(1); // 过滤器执行顺序
return registration;
}
}
2.2 拦截器的实现
拦截器接口实现:
@Component
public class CustomInterceptor implements HandlerInterceptor {
// 请求处理前调用
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("请求处理前: " + request.getRequestURI());
// 返回true继续处理,返回false中断处理
return true;
}
// 请求处理后,视图渲染前调用
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("请求处理后,视图渲染前");
// 可以修改ModelAndView对象
if (modelAndView != null) {
modelAndView.addObject("timestamp", System.currentTimeMillis());
}
}
// 整个请求完成后调用(视图渲染后)
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("请求完成后处理");
// 可以进行资源清理等操作
if (ex != null) {
System.err.println("请求处理过程中发生异常: " + ex.getMessage());
}
}
}
注册拦截器:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private CustomInterceptor customInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customInterceptor)
.addPathPatterns("/**") // 拦截所有请求
.excludePathPatterns("/static/**", "/login"); // 排除静态资源和登录请求
}
}
三、深度对比:从多维度看差异
3.1 功能特性对比表
特性 | 过滤器(Filter) | 拦截器(Interceptor) |
技术规范 | Servlet 规范 | Spring MVC 框架 |
实现机制 | 基于函数回调 | 基于动态代理(AOP) |
依赖关系 | 依赖 Servlet 容器 | 依赖 Spring 容器 |
作用范围 | 所有 HTTP 请求 | Spring MVC 处理的请求 |
执行顺序 | 按注册顺序执行 | 按配置顺序执行 |
访问上下文 | 只能访问 ServletRequest 和 ServletResponse | 可以访问 Handler、ModelAndView 等 Spring MVC 对象 |
异常处理 | 无法处理控制器抛出的异常 | 可以通过 afterCompletion 处理异常 |
应用场景 | 字符编码、敏感词过滤、请求日志 | 登录验证、权限控制、性能监控 |
3.2 执行流程可视化
过滤器执行流程:
请求进入容器
↓
过滤器1 pre-processing
↓
过滤器2 pre-processing
↓
...
↓
Servlet处理请求
↓
...
↓
过滤器2 post-processing
↓
过滤器1 post-processing
↓
响应返回客户端
拦截器执行流程:
DispatcherServlet接收到请求
↓
拦截器1 preHandle
↓
拦截器2 preHandle
↓
...
↓
控制器处理请求
↓
...
↓
拦截器2 postHandle
↓
拦截器1 postHandle
↓
视图渲染
↓
拦截器2 afterCompletion
↓
拦截器1 afterCompletion
↓
响应返回客户端
四、典型应用场景
4.1 过滤器的典型应用
1. 字符编码设置:
@WebFilter(urlPatterns = "/*")
public class EncodingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);
}
}
2. 敏感词过滤:
@WebFilter(urlPatterns = "/api/comments/*")
public class SensitiveWordFilter implements Filter {
private Set<String> sensitiveWords = new HashSet<>();
@Override
public void init(FilterConfig config) {
// 初始化敏感词库
sensitiveWords.add("垃圾");
sensitiveWords.add("笨蛋");
// ...
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
if ("POST".equalsIgnoreCase(httpRequest.getMethod())) {
// 包装请求以修改请求内容
SensitiveRequestWrapper requestWrapper = new SensitiveRequestWrapper(httpRequest, sensitiveWords);
chain.doFilter(requestWrapper, response);
} else {
chain.doFilter(request, response);
}
}
}
4.2 拦截器的典型应用
1. 登录验证:
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HttpSession session = request.getSession(false);
if (session == null || session.getAttribute("user") == null) {
// 未登录,重定向到登录页
response.sendRedirect("/login");
return false;
}
return true;
}
}
2. 性能监控:
@Component
public class PerformanceInterceptor implements HandlerInterceptor {
private ThreadLocal<Long> startTime = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
startTime.set(System.currentTimeMillis());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
long endTime = System.currentTimeMillis();
long executeTime = endTime - startTime.get();
System.out.println("请求: " + request.getRequestURI() + " 执行时间: " + executeTime + "ms");
startTime.remove(); // 防止内存泄漏
}
}
五、高级应用与最佳实践
5.1 过滤器链配置技巧
过滤器执行顺序控制:
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean<Filter1> filter1Registration() {
FilterRegistrationBean<Filter1> registration = new FilterRegistrationBean<>();
registration.setFilter(new Filter1());
registration.addUrlPatterns("/*");
registration.setName("filter1");
registration.setOrder(1); // 数字越小越先执行
return registration;
}
@Bean
public FilterRegistrationBean<Filter2> filter2Registration() {
FilterRegistrationBean<Filter2> registration = new FilterRegistrationBean<>();
registration.setFilter(new Filter2());
registration.addUrlPatterns("/*");
registration.setName("filter2");
registration.setOrder(2);
return registration;
}
}
5.2 拦截器高级应用
1. 多拦截器配置:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 登录验证拦截器
registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login", "/register");
// 性能监控拦截器
registry.addInterceptor(new PerformanceInterceptor())
.addPathPatterns("/api/**");
}
}
2. 使用拦截器实现请求限流:
@Component
public class RateLimitInterceptor implements HandlerInterceptor {
private final LoadingCache<String, AtomicInteger> counter = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.SECONDS)
.build(key -> new AtomicInteger(0));
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String ip = request.getRemoteAddr();
AtomicInteger count = counter.get(ip);
if (count.incrementAndGet() > 10) { // 每秒最多10次请求
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
response.getWriter().write("Too many requests");
return false;
}
return true;
}
}
六、性能对比与调优建议
6.1 性能对比测试
测试环境:
- CPU: Intel i7-8700K
- 内存: 16GB
- JDK: 17
- Spring Boot: 3.0.5
测试结果(执行 100 万次请求):
场景 | 过滤器(ms) | 拦截器(ms) |
空处理 | 320 | 450 |
简单业务逻辑 | 580 | 620 |
复杂业务逻辑 | 1200 | 1350 |
性能分析:
- 过滤器性能略高于拦截器,因为拦截器有 AOP 代理的额外开销
- 对于简单处理,性能差异不明显
- 随着业务逻辑复杂度增加,性能差异逐渐缩小
6.2 调优建议
过滤器优化:
- 避免在过滤器中执行复杂业务逻辑
- 合理设置过滤器顺序,减少不必要的过滤器执行
- 对大文件上传等特殊请求,考虑使用异步过滤器
拦截器优化:
- 对耗时操作使用异步处理
- 合理配置拦截路径,减少不必要的拦截器执行
- 使用 ThreadLocal 管理线程上下文时,注意及时清理
七、常见问题与解决方案
7.1 过滤器常见问题
1. 过滤器无法拦截静态资源
解决方案:
确保过滤器配置的 URL 模式包含静态资源路径,例如:/**
2. 过滤器中修改响应内容无效
解决方案:
需要使用 HttpServletResponseWrapper 包装响应对象,示例代码:
public class ResponseWrapper extends HttpServletResponseWrapper {
private ByteArrayOutputStream outputStream;
private PrintWriter writer;
public ResponseWrapper(HttpServletResponse response) {
super(response);
outputStream = new ByteArrayOutputStream();
writer = new PrintWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8));
}
@Override
public ServletOutputStream getOutputStream() {
return new ServletOutputStream() {
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener listener) {
// 实现略
}
@Override
public void write(int b) {
outputStream.write(b);
}
};
}
@Override
public PrintWriter getWriter() {
return writer;
}
public byte[] getContent() {
writer.flush();
return outputStream.toByteArray();
}
}
7.2 拦截器常见问题
1. 拦截器无法拦截请求
解决方案:
- 检查拦截器是否正确注册到 Spring MVC 配置中
- 确认拦截路径配置是否正确
- 检查 HandlerMapping 配置是否正确
2. 拦截器中无法获取 ModelAndView 数据
解决方案:
确保在postHandle方法中处理 ModelAndView,因为afterCompletion方法执行时视图已经渲染完成,ModelAndView 已不可用。
八、总结与选择指南
8.1 对比总结
维度 | 过滤器 | 拦截器 |
技术层级 | Servlet 容器层 | Spring MVC 框架层 |
实现复杂度 | 较低 | 中等 |
功能扩展性 | 有限 | 高(可利用 Spring 生态) |
性能 | 略高 | 略低 |
适用场景 | 通用请求处理 | 业务逻辑相关处理 |
8.2 选择指南
建议选择过滤器的场景:
- 需要对所有请求进行统一处理
- 实现字符编码、安全过滤等基础功能
- 不依赖 Spring 框架的独立 Web 应用
建议选择拦截器的场景:
- 需要访问 Spring MVC 上下文对象
- 实现登录验证、权限控制等业务逻辑
- 需要在请求处理的不同阶段插入逻辑
- 利用 Spring AOP 特性实现更复杂的切面功能
通过合理选择和组合使用过滤器与拦截器,可以构建高效、灵活的 Web 应用请求处理体系,提升开发效率和系统性能。