开篇:为什么你写的顺序总是不对?
在实际项目里,我们经常会同时使用多个过滤器和拦截器,例如:
- 一个负责处理跨域的
CorsFilter - 一个校验登录态的
LoginFilter - 一个打印请求日志的
LogInterceptor - 一个做权限控制的
AuthInterceptor
很多同学的第一反应是:直接给类加个 @Order(1) 就能控制顺序了吧?
结果线上却出现了:跨域过滤器没生效、登录校验被跨域挡住、日志打印顺序反了......
更诡异的是,同样的 @Order 注解,在 Filter 上可能有效,在 Interceptor 上却完全无效 。甚至你用 FilterRegistrationBean 手动注册了,顺序还是不如预期。
这背后的根本原因是:Filter 和 Interceptor 的底层机制完全不同,它们被不同的"管家"管理,排序规则自然也不同。而且,两者的执行会形成一个"包围结构"------前置正序、后置逆序。
本文会先从一次请求的执行全貌讲起,再分别拆解 Filter 和 Interceptor 的顺序控制方法,最后给出企业级实战写法和记忆口诀。读完你就能彻底告别"@Order 失效"的困惑。
一、先建立全局认知:完整请求执行链路
在动手写配置之前,务必先理解一个请求从进到出,Filter 和 Interceptor 是如何"包围"业务代码的。这是所有排序问题的基础视角。
1. 排序通用规则
order 数值越小,优先级越高,执行越靠前
无论 Filter 还是 Interceptor,最终都遵守这个数值规则,只是实现手段不同。
2. 完整请求执行链路
请求进入
→ 所有 Filter 【按 order 正序执行】 (order=1 → order=2 ... )
→ DispatcherServlet 分发
→ 所有 Interceptor preHandle 【按 order 正序执行】
→ Controller 业务方法
→ 所有 Interceptor postHandle / afterCompletion 【按 order 逆序执行】
→ 所有 Filter 【按 order 逆序执行】
→ 响应返回前端
请牢记这张图:过滤器在最外层包裹着拦截器 ;进入时正序执行,退出时逆序执行。
一旦理解了这个"包围圈"模型,后面所有的配置坑都会豁然开朗。
二、过滤器 Filter 执行顺序控制
1. 底层归属
Filter 属于 Servlet 规范,由 Tomcat 等 Servlet 容器直接管理,并非 Spring 原生组件。我们可以通过 Spring 相关的辅助类将其纳入容器,但底层仍是 Servlet 行为。
2. 三种注册方式 & @Order 生效情况
| 实现写法 | @Order 是否生效 | 适用场景 |
|---|---|---|
| @Component + 实现 Filter | ✅ 生效 | 简单单机项目 |
| FilterRegistrationBean 手动注册 | ✅ 强制生效 | 复杂项目、精准配置 |
| @WebFilter 注解注册 | ❌ 完全无效 | 不推荐使用 |
3. 实战代码示例
方式1:简单模式 @Component + @Order
Spring 直接接管 Filter 实例,在类上标注 @Order 即可控制顺序。
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
// 数值越小,越先执行
@Component
@Order(1)
public class LoginFilter implements Filter {
// 登录拦截逻辑
}
@Component
@Order(2)
public class CorsFilter implements Filter {
// 跨域处理逻辑
}
方式2:企业级推荐 FilterRegistrationBean
支持自定义拦截路径、禁用自动注册、手动指定执行顺序,控制力最强,适合生产环境。
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<LoginFilter> loginFilter() {
FilterRegistrationBean<LoginFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new LoginFilter());
bean.addUrlPatterns("/*");
bean.setOrder(1); // 明确手动指定顺序
return bean;
}
}
4. 过滤器核心避坑
- ❌ 禁止使用
@WebFilter + @Order:@WebFilter由 Tomcat 原生管理,无法识别 Spring 的@Order,顺序完全不可控; - ✅ 统一规范 :复杂业务一律使用
FilterRegistrationBean注册,并可配合setOrder()精准控制。
三、拦截器 Interceptor 执行顺序控制
1. 底层归属
Interceptor 属于 Spring MVC 原生组件 ,仅拦截由 DispatcherServlet 分发的 Controller 请求,不会拦截静态资源(如 /static/** 下的文件)。
2. 高频误区:为什么拦截器类上加 @Order 无效?
很多新手会这样写:
// ❌ 完全无效!错误写法
@Order(1)
@Component
public class LoginInterceptor implements HandlerInterceptor {
// ...
}
根本原因:
- Interceptor 最终是由
InterceptorRegistry统一注册并维护的,Spring MVC 框架内部自行管理这个拦截器集合; - 普通的 Bean 排序(靠
@Order或Ordered接口)对 Spring MVC 拦截器链完全不起作用; - 框架并不会根据拦截器 Bean 的优先级去调整注册顺序。
3. 拦截器两种正确排序方式
方式1【官方推荐】:注册时调用 .order()
在同一个配置类中集中注册多个拦截器,直接链式调用 order() 方法,清晰且无坑。
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 数值越小,preHandle 越先执行
registry.addInterceptor(new LoginInterceptor()).order(1);
registry.addInterceptor(new LogInterceptor()).order(2);
}
}
方式2:多配置类 + 配置类上加 @Order
当项目需要将不同拦截器拆分到多个 WebMvcConfigurer 配置类时,可以给配置类 加 @Order 来间接控制顺序。
// 第一个配置类,优先级高
@Order(1)
@Configuration
public class InterceptorConfig1 implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor());
}
}
// 第二个配置类,优先级低
@Order(2)
@Configuration
public class InterceptorConfig2 implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor());
}
}
4. 拦截器特殊执行顺序
preHandle:正序执行(order 1 → order 2)postHandle/afterCompletion:逆序执行(order 2 → order 1)
这与前面全局执行链路的"包围"模型完全吻合。
四、Filter 与 Interceptor 核心对比
为方便记忆,这里汇总两者的关键差异:
| 对比维度 | 过滤器 Filter | 拦截器 Interceptor |
|---|---|---|
| 所属规范 | Servlet 容器规范 | Spring MVC 框架 |
| 排序注解 | @Order 直接生效(Component 方式) | 类上 @Order 无效 |
| 排序API | setOrder() | 注册时调用 .order() |
| 生效范围 | 全部请求(静态资源+接口) | 仅 Controller 接口请求 |
| 执行时机 | 最早拦截、最晚放行 | Controller 前后拦截 |
五、极简记忆口诀
- 过滤器 :
@Order直接可用,@WebFilter千万别混用 - 拦截器 :自身注解全无效,注册
order最可靠 - 通用规则:数值越小,执行越靠前;前置正序,后置逆序
六、最终总结
- Filter 过滤器
- 简单场景:
@Component + @Order快速实现排序; - 生产规范:统一使用
FilterRegistrationBean+setOrder()精细化配置; - 严禁使用
@WebFilter做顺序控制。
- 简单场景:
- Interceptor 拦截器
- 永远不要直接给拦截器实现类加
@Order; - 优先采用
addInterceptor().order()链式写法,简单可靠; - 多配置类场景,可通过给配置类加
@Order间接控制。
- 永远不要直接给拦截器实现类加
- 整体链路牢记
- 过滤器在最外层包围拦截器;
- 请求进入时正序执行,响应返回时逆序执行。