这是一个非常经典的Spring面试题,能很好地区分候选人对Web请求处理流程的理解深度。下面我通过定义、执行顺序、作用范围、应用场景和代码实现五个维度进行详细对比。
一、核心对比一览表
| 对比维度 | 过滤器 (Filter) | 拦截器 (Interceptor) |
|---|---|---|
| 所属规范 | Servlet 规范 (J2EE标准) | Spring 框架 定义 |
| 依赖关系 | 不依赖Spring,可用于任何Web应用 | 依赖Spring容器 |
| 作用范围 | 过滤所有请求(包括静态资源) | 只过滤Spring管理的Controller请求 |
| 实现方式 | 实现 javax.servlet.Filter 接口 |
实现 HandlerInterceptor 接口 |
| 执行位置 | Servlet容器(Tomcat/Jetty)层面 | Spring MVC框架层面 |
| 注入Bean | 不能直接注入Spring Bean | 可以直接注入Spring Bean |
| 触发时机 | 在Servlet 之前 和之后执行 | 在Controller 方法前后执行 |
二、执行流程与顺序(最关键的差异)
完整请求处理链
图表
代码
复制
下载
全屏
flowchart LR
A[HTTP请求] --> B[Servlet容器<br>接收请求]
B --> C[Filter Chain<br>过滤器链]
subgraph C [过滤器执行 - Servlet层面]
C1[Filter1<br>doFilter]
C2[Filter2<br>doFilter]
C3[...]
end
C --> D[DispatcherServlet<br>Spring MVC前端控制器]
D --> E[HandlerMapping<br>找到对应Controller]
E --> F[Interceptor Chain<br>拦截器链]
subgraph F [拦截器执行 - Spring层面]
direction LR
F1[PreHandle]
F2[PreHandle]
F3[...]
end
F --> G[Controller<br>执行业务逻辑]
G --> H[返回ModelAndView]
subgraph I [拦截器后处理]
I1[PostHandle]
I2[PostHandle]
I3[...]
end
H --> I
I --> J[视图渲染]
J --> K[Filter Chain<br>过滤器后处理]
subgraph L [拦截器最终处理]
L1[AfterCompletion]
L2[AfterCompletion]
L3[...]
end
J --> L
L --> M[HTTP响应]
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
详细执行时序
text
复制
下载
客户端请求
↓
1. Servlet容器接收请求
↓
2. 执行所有 **Filter.doFilter()** 的前置逻辑
↓
3. 请求到达 **DispatcherServlet**
↓
4. HandlerMapping找到对应Controller
↓
5. 执行 **Interceptor.preHandle()** (按配置顺序)
↓
6. **Controller方法执行**
↓
7. 执行 **Interceptor.postHandle()** (按配置逆序)
↓
8. 视图渲染(如JSP、Thymeleaf)
↓
9. 执行 **Interceptor.afterCompletion()** (按配置逆序)
↓
10. 执行所有 **Filter.doFilter()** 的后置逻辑
↓
11. 返回响应给客户端
关键记忆点:
-
Filter包裹Interceptor:Filter是最外层,Interceptor在内层
-
Controller只在最中心:Interceptor包裹着Controller方法
-
逆序执行:postHandle和afterCompletion是逆序的
三、代码实现对比
过滤器 (Filter) 实现
java
复制
下载
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
// 方式1:注解配置(Servlet 3.0+)
@WebFilter(urlPatterns = "/*", filterName = "logFilter")
// 方式2:通过FilterRegistrationBean配置(Spring Boot推荐)
public class LogFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
// 初始化方法,容器启动时调用一次
System.out.println("LogFilter初始化");
}
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
long startTime = System.currentTimeMillis();
System.out.println("Filter开始: " +
((HttpServletRequest)request).getRequestURI());
// 1. 前置处理(Controller之前)
// 可以修改Request/Response,如设置字符编码
request.setCharacterEncoding("UTF-8");
// 放行到下一个过滤器或Servlet
chain.doFilter(request, response);
// 2. 后置处理(Controller之后)
long endTime = System.currentTimeMillis();
System.out.println("Filter结束,耗时: " + (endTime - startTime) + "ms");
}
@Override
public void destroy() {
// 销毁方法,容器关闭时调用
System.out.println("LogFilter销毁");
}
}
// Spring Boot配置方式(推荐)
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<LogFilter> logFilter() {
FilterRegistrationBean<LogFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new LogFilter());
bean.addUrlPatterns("/*");
bean.setOrder(1); // 设置执行顺序
bean.setName("logFilter");
return bean;
}
}
拦截器 (Interceptor) 实现
java
复制
下载
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class AuthInterceptor implements HandlerInterceptor {
// 注入Spring Bean(过滤器不能直接注入)
@Autowired
private UserService userService;
/**
* Controller方法执行前调用
* 返回值:true-继续执行,false-中断执行
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("Interceptor preHandle: " + request.getRequestURI());
// 示例:权限校验
String token = request.getHeader("Authorization");
if (!userService.validateToken(token)) {
response.setStatus(401);
response.getWriter().write("Unauthorized");
return false; // 中断请求,不执行Controller
}
// 设置请求属性,Controller中可以获取
request.setAttribute("startTime", System.currentTimeMillis());
return true; // 继续执行
}
/**
* Controller方法执行后,视图渲染前调用
* 可以修改ModelAndView
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("Interceptor postHandle");
if (modelAndView != null) {
// 可以往Model中添加全局数据
modelAndView.addObject("serverTime", System.currentTimeMillis());
}
}
/**
* 整个请求完成后调用(视图渲染完毕)
* 适合进行资源清理、异常日志记录
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
long startTime = (Long) request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
System.out.println("请求处理完成,耗时: " + (endTime - startTime) + "ms");
if (ex != null) {
// 记录异常日志
System.err.println("请求异常: " + ex.getMessage());
}
}
}
// 注册拦截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/api/**") // 拦截路径
.excludePathPatterns("/api/login", "/api/register") // 排除路径
.order(1); // 执行顺序
}
}
四、应用场景选择指南
使用过滤器的场景 ✅
-
全局字符编码处理
java
复制
下载
// 在过滤器中统一设置UTF-8编码 request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); -
跨域请求处理 (CORS)
java
复制
下载
// 添加CORS头 response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "*"); -
XSS/SQL注入防护
java
复制
下载
// 对请求参数进行全局过滤 XssHttpServletRequestWrapper wrappedRequest = new XssHttpServletRequestWrapper(request); chain.doFilter(wrappedRequest, response); -
静态资源缓存控制
java
复制
下载
if (request.getRequestURI().endsWith(".js")) { response.setHeader("Cache-Control", "max-age=31536000"); } -
请求/响应日志记录(所有请求,包括静态资源)
使用拦截器的场景 ✅
-
用户身份认证与授权
java
复制
下载
// 检查Session或Token if (request.getSession().getAttribute("user") == null) { response.sendRedirect("/login"); return false; } -
接口访问频率限制
java
复制
下载
// 基于IP或用户ID的限流 String key = "rate_limit:" + getClientIp(request); if (redisTemplate.opsForValue().increment(key) > 100) { return false; // 超过限制 } -
Controller方法执行时间监控
java
复制
下载
// 在preHandle记录开始时间 // 在afterCompletion计算耗时并上报监控系统 -
统一异常处理增强
java
复制
下载
// 在afterCompletion中记录异常到日志系统 -
业务相关的预处理
java
复制
下载
// 如:解析设备类型、设置时区、封装用户上下文等
实际项目中的典型配置
java
复制
下载
@Configuration
public class WebConfig {
/**
* 过滤器配置:处理底层、通用的Web请求
*/
@Bean
public FilterRegistrationBean<CharacterEncodingFilter> encodingFilter() {
FilterRegistrationBean<CharacterEncodingFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new CharacterEncodingFilter("UTF-8", true));
bean.addUrlPatterns("/*");
bean.setOrder(Ordered.HIGHEST_PRECEDENCE); // 最高优先级
return bean;
}
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
// CORS过滤器配置
return new FilterRegistrationBean<>(new CorsFilter());
}
/**
* 拦截器配置:处理业务相关的请求
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 1. 日志拦截器(最外层)
registry.addInterceptor(new LogInterceptor())
.addPathPatterns("/**")
.order(1);
// 2. 认证拦截器
registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns("/api/public/**")
.order(2);
// 3. 性能监控拦截器(最内层)
registry.addInterceptor(new PerformanceInterceptor())
.addPathPatterns("/api/**")
.order(3);
}
}
五、面试深度回答要点
基础回答(校招/初级)
"过滤器是Servlet规范定义的,在Servlet容器层面工作,可以过滤所有请求包括静态资源;拦截器是Spring框架定义的,在Spring MVC层面工作,只能拦截Controller请求。过滤器通过doFilter方法,拦截器通过preHandle、postHandle、afterCompletion三个方法。"
进阶回答(社招/中级)
"从执行顺序看,过滤器在最外层,拦截器在内层。过滤器可以修改Request/Response,拦截器可以获取到Handler和ModelAndView。过滤器不能注入Spring Bean,拦截器可以。过滤器适合做全局的、与业务无关的处理(如编码、CORS),拦截器适合做业务相关的处理(如权限、日志)。"
深度回答(高级/架构师)
"这本质上是责任链模式在不同层面的实现。过滤器是Servlet容器的责任链,拦截器是Spring MVC的责任链。在设计上,过滤器关注的是HTTP请求/响应的原始处理,拦截器关注的是MVC流程中的业务切面。生产环境中,我们通常用过滤器处理基础设施层的问题(安全、编码、压缩),用拦截器处理应用层的问题(认证、审计、性能监控)。对于微服务架构,许多过滤器的功能可以下沉到API网关。"
扩展问题准备
-
如何保证多个过滤器/拦截器的执行顺序?
-
过滤器:通过FilterRegistrationBean.setOrder()
-
拦截器:通过InterceptorRegistry.addInterceptor().order()
-
-
拦截器preHandle返回false后,postHandle还会执行吗?
- 不会。preHandle返回false会中断整个执行链。
-
过滤器中抛出异常会怎样?
- 如果没有异常处理,会返回500错误,后续过滤器和拦截器不会执行。
-
可以同时修改Request和Response吗?
- 过滤器可以,拦截器主要修改Response,修改Request需要通过包装类。
-
Spring Boot中如何禁用默认过滤器?
properties
复制
下载
# 禁用Spring Security的默认过滤器 security.basic.enabled=false # 或通过配置类排除 @ComponentScan(excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {SomeFilter.class}))
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
掌握这个区别不仅为了面试,更重要的是在实际项目中能够合理设计请求处理链,构建清晰、可维护的Web应用架构。