1. 从Servlet到SpringBoot:请求处理的演进脉络
在Java Web开发的历史长河中,请求处理机制经历了多次迭代演进。早期的Servlet规范中,Filter作为唯一的请求预处理机制存在,它像是守门人,对所有进入容器的请求进行第一道安检。随着Spring框架的兴起,Interceptor作为一种更灵活的拦截机制被引入,它工作在Spring的上下文环境中,能够感知Spring管理的Bean。
SpringBoot的自动配置机制进一步简化了这两种组件的使用。通过查看WebMvcAutoConfiguration源码可以发现,SpringBoot会自动注册HandlerInterceptor和Filter到Servlet容器中。这种自动装配的特性使得开发者可以更专注于业务逻辑的实现,而不必关心底层组件的注册细节。
关键区别:Filter是Servlet规范定义的,Interceptor是Spring框架定义的。这意味着即使不使用Spring框架,Filter依然可以工作,而Interceptor必须运行在Spring上下文中。
2. 执行时机与作用范围的深度对比
2.1 请求处理流程的全景分析
一个典型的HTTP请求在SpringBoot应用中的处理流程如下:
-
Filter阶段:
doFilter()开始- 字符编码设置
- 跨域请求处理
- 请求体缓存(用于多次读取)
-
DispatcherServlet分发:
- 确定HandlerMapping
- 解析请求参数
-
Interceptor阶段:
preHandle()执行- 权限校验
- 日志记录
- 参数预处理
-
Controller处理:
- 业务逻辑执行
- 返回结果封装
-
Interceptor后处理:
postHandle()执行- 响应数据加工
- 性能监控
-
Filter收尾:
doFilter()结束- 响应头设置
- 响应体压缩
2.2 核心差异对照表
| 特性 | Filter | Interceptor |
|---|---|---|
| 规范层级 | Servlet规范 | Spring框架 |
| 执行时机 | 进入Servlet前/离开Servlet后 | Controller方法前后 |
| 依赖关系 | 不依赖Spring容器 | 需要Spring上下文 |
| 异常处理 | 无法获取Controller抛出的异常 | 可以通过afterCompletion获取异常 |
| 配置方式 | web.xml或@WebFilter | 实现WebMvcConfigurer |
| 访问范围 | 只能获取ServletRequest/Response | 可以获取HandlerMethod信息 |
| 执行顺序 | 通过@Order控制 | 通过order属性控制 |
| 性能影响 | 影响所有请求 | 只影响匹配的请求 |
3. 实战中的典型应用场景
3.1 Filter的最佳实践场景
**跨域处理(CORS)**是Filter的经典用例。虽然Spring提供了@CrossOrigin注解,但在微服务架构中,通常需要在网关层统一处理:
java复制@WebFilter("/*")
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
chain.doFilter(req, res);
}
}
请求体缓存是另一个Filter的重要应用。当需要多次读取请求体(如签名验证和业务处理都需要读取body)时:
java复制public class ContentCachingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(
(HttpServletRequest) request);
chain.doFilter(wrappedRequest, response);
}
}
3.2 Interceptor的典型使用模式
权限校验是Interceptor最常见的应用。相比Filter,它可以获取到Controller方法的注解信息:
java复制public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler;
RequireAuth auth = method.getMethodAnnotation(RequireAuth.class);
if (auth != null && !checkAuth(request)) {
response.sendError(401, "Unauthorized");
return false;
}
}
return true;
}
}
接口耗时统计展示了Interceptor在AOP方面的优势:
java复制public class TimingInterceptor implements HandlerInterceptor {
private static final 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 duration = System.currentTimeMillis() - startTime.get();
log.info("{} {} took {}ms", request.getMethod(), request.getRequestURI(), duration);
}
}
4. 高级特性与常见陷阱
4.1 执行顺序的精确控制
在复杂应用中,多个Filter和Interceptor的执行顺序至关重要。SpringBoot提供了多种控制方式:
- Filter顺序控制:
- 使用
@Order注解指定顺序值 - 实现
Ordered接口 - 在
FilterRegistrationBean中设置order属性
- 使用
java复制@Bean
public FilterRegistrationBean<LogFilter> loggingFilter() {
FilterRegistrationBean<LogFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new LogFilter());
registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
return registration;
}
- Interceptor顺序控制:
- 在
WebMvcConfigurer实现中,添加顺序决定了执行顺序 - 可以通过
order属性调整
- 在
java复制@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor()).order(1);
registry.addInterceptor(new LogInterceptor()).order(2);
}
4.2 异常处理机制的差异
Filter和Interceptor在异常处理方面有显著不同:
- Filter:无法直接捕获Controller抛出的异常,但可以通过包装Response来统一处理错误
- Interceptor:
afterCompletion方法可以获取Controller抛出的异常,但无法修改响应
典型错误:试图在Interceptor的postHandle中处理异常(此时异常还未抛出)。正确的做法是:
java复制@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
if (ex != null) {
// 异常处理逻辑
}
}
4.3 性能优化建议
-
Filter优化:
- 对静态资源请求添加路径排除(
urlPatterns) - 避免在Filter中进行复杂业务逻辑
- 考虑使用
OncePerRequestFilter确保单次执行
- 对静态资源请求添加路径排除(
-
Interceptor优化:
- 使用
PathMatcher精确匹配需要拦截的路径 - 将频繁使用的数据缓存到请求属性中
- 避免在
preHandle中进行IO操作
- 使用
5. 与AOP的对比及组合使用
虽然Interceptor和AOP切面都可以实现横切关注点,但它们有本质区别:
| 维度 | Interceptor | AOP Aspect |
|---|---|---|
| 工作层级 | MVC层面 | 方法调用层面 |
| 获取信息 | 请求/响应对象 | 方法参数/返回值 |
| 执行时机 | 仅Controller方法前后 | 任意Spring Bean方法前后 |
| 配置方式 | 实现接口+配置类 | 注解+表达式 |
| 性能影响 | 较小 | 较大(动态代理开销) |
组合使用示例:在微服务架构中,可以这样分工:
- Filter处理基础设施层问题(CORS、缓存、压缩)
- Interceptor处理业务层横切关注点(权限、日志)
- AOP处理服务层通用逻辑(事务、缓存、重试)
java复制// 组合使用示例
@Aspect
@Component
public class ServiceLogAspect {
@Around("execution(* com.example..service.*.*(..))")
public Object logServiceCall(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
log.debug("Service method {} executed in {}ms",
pjp.getSignature(), System.currentTimeMillis() - start);
}
}
}
在实际项目中,我通常会建立这样的拦截体系:Filter处理最基础的请求/响应转换,Interceptor处理业务相关的预处理,AOP处理服务层的通用逻辑。这种分层设计使得各组件职责单一,便于维护和扩展。
