侧边栏壁纸
博主头像
Setyg的博客

当秋风染红落叶的时候,就是新生~

  • 累计撰写 86 篇文章
  • 累计创建 66 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Spring Boot 多个 Filter 指定执行顺序实践

Setyg
2026-09-14 / 0 评论 / 0 点赞 / 5 阅读 / 0 字

Spring Boot 多个 Filter 指定执行顺序实践

项目中经常同时存在多个 Filter(过滤器),比如登录校验、请求日志、跨域处理、XSS 过滤等。如果不显式指定顺序,它们的执行先后是不确定的,容易出现「日志里拿不到登录用户信息」「CORS 响应头被覆盖」「次数统计记在了鉴权之前」这类难以排查的问题。

本文整理 Spring Boot 中指定多个 Filter 执行顺序的三种方式,并给出对比、顺序规则与常见失效原因。

一、执行顺序的核心规则

Spring Boot 中 Filter 的执行顺序由 order 值决定:

  • 数字越小,优先级越高,越先执行;

  • 请求进入时,按 order 升序依次执行各 Filter 中 chain.doFilter() 之前的代码;

  • 响应返回时,按相反顺序执行 chain.doFilter() 之后的代码,整体是「栈式」结构;

  • 未指定 order 的 Filter,默认取 Ordered.LOWEST_PRECEDENCE(即 Integer.MAX_VALUE),排在最后。

二、方式一:@Order 注解

直接在实现了 Filter 接口的类上使用 @Order 注解,最为简洁。

代码示例

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

@Component
@Order(1) // 优先级高
public class FirstFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化代码
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 过滤逻辑
        System.out.println("FirstFilter is executed");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // 清理资源
    }
}

@Component
@Order(2) // 优先级低
public class SecondFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化代码
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 过滤逻辑
        System.out.println("SecondFilter is executed");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // 清理资源
    }
}

说明

  • 适用于「自己编写 + 交给 Spring 管理」的 Filter;

  • 底层由 ServletContextInitializerBeans 收集所有 Filter 类型的 Bean,再通过 AnnotationAwareOrderComparator 排序,所以 @Order 才能被识别;

  • 前提是 Filter 类必须被 Spring 扫描到(带 @Component)。若是第三方库通过 web.xml 或 Servlet 容器注册的 Filter,@Order 不会生效。

三、方式二:实现 Ordered 接口

@Order 的本质就是 Ordered 接口的注解版。当顺序值需要由代码动态计算(例如根据配置、环境决定)时,直接实现接口更灵活。

import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

@Component
public class SecondFilter implements Filter, Ordered {

    @Override
    public int getOrder() {
        return 2; // 数字越小,优先级越高
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("SecondFilter is executed");
        chain.doFilter(request, response);
    }
}

优先级说明:当同一个类上同时存在 @Order 和 getOrder() 时,Spring 的排序比较器会优先读取注解值,因此两者不要同时使用,避免混淆。

四、方式三:FilterRegistrationBean(推荐)

如果 Filter 类没有被 Spring 管理(例如来自第三方库、需要 new 出来),或者需要按 URL 路径精确控制生效范围,推荐显式注册。

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<FirstFilter> firstFilterRegistration() {
        FilterRegistrationBean<FirstFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new FirstFilter());
        registration.addUrlPatterns("/*");
        registration.setName("firstFilter");
        registration.setOrder(1); // 数字越小,优先级越高
        return registration;
    }

    @Bean
    public FilterRegistrationBean<SecondFilter> secondFilterRegistration() {
        FilterRegistrationBean<SecondFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new SecondFilter());
        registration.addUrlPatterns("/api/*");
        registration.setName("secondFilter");
        registration.setOrder(2);
        return registration;
    }
}

常用配置项

方法

作用

setFilter()

设置目标 Filter 实例

addUrlPatterns()

指定拦截路径,可多次调用追加,如 /*、/api/*

setName()

Filter 名称,便于日志与排查

setOrder()

执行顺序,数字越小越先执行

setEnabled()

是否启用,常用于配置开关

setInitParameters()

设置初始化参数

注意:用这种方式注册后,Filter 类本身不要再加 @Component,否则会被 Spring Boot 自动注册一次、又被手动注册一次,导致同一个 Filter 执行两遍。

五、三种方式对比

方式

适用场景

能否指定 URL

推荐度

@Order 注解

自研 Filter,Spring 管理的 Bean

否

⭐⭐⭐ 简单场景首选

实现 Ordered

顺序值需动态计算

否

⭐⭐ 特殊场景

FilterRegistrationBean

第三方 Filter、需按路径控制

是

⭐⭐⭐ 复杂场景推荐

六、常见问题排查

1. 为什么 @Order 没生效?

  • Filter 类缺少 @Component,压根没被 Spring 扫描到;

  • Filter 是通过 @Bean 方法返回的 —— 此时 @Order 标注在类上无效,需要把 @Order 加到 @Bean 方法上,或改用 FilterRegistrationBean;

  • Filter 由第三方 Starter 自动配置注册,顺序被框架固定。

2. 与 Spring Security 的先后关系

Spring Security 的过滤器链(springSecurityFilterChain)默认 order 是 -100(常量 SecurityProperties.DEFAULT_FILTER_ORDER)。因此自定义 Filter 若想先于 Security 执行,order 需要小于 -100;想后于 Security 执行,order 需要大于 -100。

3. javax 还是 jakarta?

Spring Boot 2.x 使用的是 javax.servlet;从 Spring Boot 3.x 起已迁移到 jakarta.servlet。升级版本时记得同步替换 import 包名,否则会出现类找不到或 Filter 完全不执行的问题。

4. order 值相同会怎样?

order 值相同时执行顺序不确定,取决于 Bean 的注册顺序,且不同机器/不同版本可能不一致。建议为每个 Filter 分配不同值,并按 10、20、30 的步长留出插入空间。

5. Filter 与 Interceptor 的顺序

Filter 属于 Servlet 规范,在 DispatcherServlet 之前执行;Interceptor 属于 Spring MVC,在 DispatcherServlet 之后执行。所以无论 order 怎么设置,Filter 一定先于所有 Interceptor。

七、验证执行顺序

为两个 Filter 的 doFilter 前后各加一行日志,访问任意接口后观察输出:

FirstFilter is executed
SecondFilter is executed
SecondFilter is done
FirstFilter is done

可以看到:请求方向与响应方向刚好相反,这就是前面提到的「栈式」结构。理解这一点,对处理「响应头包装」「请求体重复读取」「耗时统计」等场景非常关键 —— 统计耗时的代码必须放在 chain.doFilter() 之后才能拿到完整时间。

八、小结

  1. 顺序规则只有一条:order 数字越小越先执行,响应阶段反向回流。

  2. 简单的自研 Filter 用 @Order 即可,别忘了 @Component。

  3. 需要按 URL 路径控制、或 Filter 不受 Spring 管理时,用 FilterRegistrationBean 显式注册,并避免重复注册。

  4. 与 Spring Security 配合时,记住基准值是 -100。

  5. 给 order 留出步长,避免撞车导致顺序不可控。

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区