Spring Boot Filter过滤器的用法(非常详细)
过滤器(Filter)是 Web 开发中很实用的一项技术,开发人员可以通过过滤器对 Web 服务管理的资源静态 HTML 文件、静态图片、JSP、Servlet 等进行拦截,从而实现一些特殊的需求,比如设置 URL 的访问权限、过滤敏感词汇、压缩响应信息等。
过滤器还适用于对用户请求和响应对象进行检查和修改,但是 Filter 本身并不生成请求和响应对象,只是提供过滤功能。
Filter 的完整工作流程如下图所示:
当客户端发出对 Web 资源的请求时,Web 服务器会根据应用程序配置文件设置的过滤规则进行检查,若客户端请求满足过滤规则,则对客户端请求/响应进行拦截:
这就是过滤器的工作原理。
另外,过滤器的生命周期也是由 Web 服务器进行负责的,但是相比真正的 Servlet 又有区别。Filter 的生命周期大致分为以下三个阶段:
定义的过滤器 WebTestFilter.java 必须实现 javax.servlet.Filter 接口,根据自身需求重写 doFilter() 方法。
另外,在启动类上加一个注解 @ServletComponentScan。其中,urlPatterns 是配置过滤器要过滤的 URL,可支持模糊匹配;filterName 只是定义的过滤器名字;@Order(int)注解主要用于多个过滤器时定义执行顺序,值越小越先执行(后面将会详细讲解配置多个Filter的执行顺序)。
参考代码如下:
过滤器类的参考代码如下:
过滤器配置类的参考代码如下:
按理说,通过 @Order 注解指定一个 int 值,越小越先执行,但是 AFilter.java 中定义的执行顺序是 order(2),WebTestFilter.java 中定义的执行顺序是 order(1),结果显示先执行的是 AFilter.java 过滤器,说明 @Order(int) 注解和 @WebFilter 注解一起使用的时候并未生效。
我们可以通过源码发现,@WebFilter 修饰的过滤器在加载时并未使用 @Order 注解,而是使用类名来自定义 Filter 的执行顺序,正如当下两个比对的过滤器,执行顺序 AFilter.java > WebTestFilter.java。
从上面的例子我们会发现,如果想以 @WebFilter 方式定义多个过滤器并且限定过滤器执行顺序,就必须限定 Filter 的类名了。以上情况对开发者来说并不友好,所以建议使用配置类注册过滤器的方式配置多个过滤器。
1) 新增一个过滤器,参考代码如下:
2) 对应修改配置类 WebFilterConfig.java,如下代码所示:
运行项目,访问 http://localhost:8080/html/test.html,测试结果如下图所示:
正如我们配置的顺序,先执行的是过滤器 RequestTest2Filter.java,后执行的是 RequestTest1Filter.java。
另外,对上面的代码做一些说明:
1) 对于 frBean.addInitParameter("name"," filter-test2"),设置的参数在 Filter 的 init 方法里的 FilterConfig 对象里可以获取,即 filterConfig.getInitParameter("name"),但是初始化时获取的过滤器名字和设定的执行顺序 order 无关,而是根据默认的类加载顺序而来的。
2) 对于 frBean.setUrlPatterns(urls),可以设置多个 URL 匹配规则,setUrlPatterns 接收一个 List<String> 类型的参数。其实现代码在 WebFilterConfig.java 中注册 RequestTest2Filter.java 时已经给出相关示例。
3) 当不设置 setOrder 次序时,过滤器的执行顺序默认是 Bean 的加载顺序。在当前 WebConfig 类中,先加载 RequestTest1Filter.java 过滤器,后加载 RequestTest2Filter.java 过滤器。
过滤器还适用于对用户请求和响应对象进行检查和修改,但是 Filter 本身并不生成请求和响应对象,只是提供过滤功能。
Filter 的完整工作流程如下图所示:

当客户端发出对 Web 资源的请求时,Web 服务器会根据应用程序配置文件设置的过滤规则进行检查,若客户端请求满足过滤规则,则对客户端请求/响应进行拦截:
- 首先按照需求对请求头和请求数据进行封装,并依次通过过滤器链;
- 然后把请求/响应交给 Web 资源处理,请求信息在过滤器链中可以被修改,也可以根据条件让请求不发往资源处理器,并直接向客户机发回一个响应;
- 当资源处理器完成了对资源的处理后,响应信息将逐级逆向返回。在这个过程中,用户可以修改响应信息,从而完成一定的任务。
这就是过滤器的工作原理。
另外,过滤器的生命周期也是由 Web 服务器进行负责的,但是相比真正的 Servlet 又有区别。Filter 的生命周期大致分为以下三个阶段:
- 实例化:Web 容器在部署 Web 应用程序时对所有过滤器进行实例化,此时 Web 容器调用的是它的无参构造方法。
- 初始化:实例化完成之后,马上进行初始化工作。Web容器回调init()方法。请求路径匹配过滤器的 URL 映射时,Web 容器回调过滤器的 doFilter() 方法,此方法也是过滤器的核心方法。
- 销毁:Web 容器在卸载 Web 应用程序前回调 doDestory 方法。
@WebFilter实现自定义过滤器
自定义过滤器常用的有两种方式,这里主要讲解通过 @WebFilter 和 @ServletComponentScan 实现过滤器。定义的过滤器 WebTestFilter.java 必须实现 javax.servlet.Filter 接口,根据自身需求重写 doFilter() 方法。
另外,在启动类上加一个注解 @ServletComponentScan。其中,urlPatterns 是配置过滤器要过滤的 URL,可支持模糊匹配;filterName 只是定义的过滤器名字;@Order(int)注解主要用于多个过滤器时定义执行顺序,值越小越先执行(后面将会详细讲解配置多个Filter的执行顺序)。
参考代码如下:
/** * 测试单个过滤器 */ @WebFilter(urlPatterns = "/html/*", filterName = "WebFilter") @Order(1) public class WebTestFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("WebTestFilter"); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } }启动项目,访问要过滤的资源(此处以 http://localhost:8080/html/test.html 为例),就可以看到控制台打印的相关内容。如下图所示:

自定义配置类配置过滤器
自定义配置类配置过滤器简而言之就是定义一个过滤器,实现 javax.servlet.Filter 接口,然后将此过滤器以 @Bean 的形式注册到配置类中。过滤器类的参考代码如下:
/** * 过滤器,测试案例1 */ public class RequestTestlFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { String filterName = filterConfig.getInitParameter("name"); System.out.println("过滤器测试案例1,name:" + filterName); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("testl-filter"); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } }
过滤器配置类的参考代码如下:
/** * 配置过滤器 */ @Configuration public class WebFilterConfig { @Bean public FilterRegistrationBean filterRegistTestl() { FilterRegistrationBean<Filter> frBean = new FilterRegistrationBean<>(); RequestTestlFilter requestTestlFilter = new RequestTestlFilter(); frBean.setFilter(requestTestlFilter); frBean.addUrlPatterns("/html/*"); frBean.addInitParameter("name", "filter-testl"); frBean.setName("requestTestlFilter"); frBean.setOrder(4); return frBean; } }此时只要保证 WebFilterConfig.java 被扫描到即可,即配置类上增加 @Configuration 注解,然后启动 Spring Boot 项目即可看到过滤器里定义的内容。此处请求 http://localhost:8080/html/test.html 测试,结果如下图所示:第一行打印的是过滤器名字,第二行是拦截时执行的内容。

配置多个过滤器
对于通过 @WebFilter 定义的多个过滤器,我们真的能通过 @Order(int) 注解实现自定义执行顺序吗?我们可以通过下面的例子测试一下:/** * 用于测试 order 定义的过滤器顺序 */ @WebFilter(urlPatterns = "/html/*", filterName = "AFilter") @Order(2) public class AFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("AFilter"); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } }运行项目,访问 http://localhost:8080/html/test.html,对比 AFilter.java 和 WebTestFilter.java 两个过滤器执行的顺序,结果如下图所示。我们发现先执行的是过滤器 AFilter.java,后执行的是 WebTestFilter.java。

按理说,通过 @Order 注解指定一个 int 值,越小越先执行,但是 AFilter.java 中定义的执行顺序是 order(2),WebTestFilter.java 中定义的执行顺序是 order(1),结果显示先执行的是 AFilter.java 过滤器,说明 @Order(int) 注解和 @WebFilter 注解一起使用的时候并未生效。
我们可以通过源码发现,@WebFilter 修饰的过滤器在加载时并未使用 @Order 注解,而是使用类名来自定义 Filter 的执行顺序,正如当下两个比对的过滤器,执行顺序 AFilter.java > WebTestFilter.java。
从上面的例子我们会发现,如果想以 @WebFilter 方式定义多个过滤器并且限定过滤器执行顺序,就必须限定 Filter 的类名了。以上情况对开发者来说并不友好,所以建议使用配置类注册过滤器的方式配置多个过滤器。
1) 新增一个过滤器,参考代码如下:
/** * 过滤器,测试案例2 */ public class RequestTest2Filter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { String filterName = filterConfig.getInitParameter("name"); System.out.println("过滤器测试案例2,name:" + filterName); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("test2-filter"); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } }
2) 对应修改配置类 WebFilterConfig.java,如下代码所示:
/** * 配置过滤器,测试过滤器执行顺序 */ @Configuration public class WebFilterConfig { @Bean public FilterRegistrationBean filterRegistTestl() { FilterRegistrationBean<Filter> frBean = new FilterRegistrationBean<>(); RequestTestlFilter requestTestlFilter = new RequestTestlFilter(); frBean.setFilter(requestTestlFilter); frBean.addUrlPatterns("/html/*"); frBean.addInitParameter("name", "filter-test1"); frBean.setName("requestTestlFilter"); frBean.setOrder(2); return frBean; } @Bean public FilterRegistrationBean filterRegistTest2() { FilterRegistrationBean<Filter> frBean = new FilterRegistrationBean<>(); RequestTest2Filter requestTest2Filter = new RequestTest2Filter(); frBean.setFilter(requestTest2Filter); // frBean.addUrlPatterns("/html/*"); // 配置多个过滤规则 ArrayList<String> urlList = new ArrayList<>(); urlList.add("/html/test.html"); urlList.add("/html/login.html"); frBean.setUrlPatterns(urlList); frBean.addInitParameter("name", "filter-test2"); frBean.setName("requestTest2Filter"); frBean.setOrder(1); return frBean; } }将两个过滤器 RequestTest1Filter.java 和 RequestTest2Filter.java 注册到配置类 WebFilterConfig.java中,指定 RequestTest1Filter.java 的执行顺序 order为2、RequestTest1Filter.java 的执行顺序 order 为 1。
运行项目,访问 http://localhost:8080/html/test.html,测试结果如下图所示:

正如我们配置的顺序,先执行的是过滤器 RequestTest2Filter.java,后执行的是 RequestTest1Filter.java。
另外,对上面的代码做一些说明:
1) 对于 frBean.addInitParameter("name"," filter-test2"),设置的参数在 Filter 的 init 方法里的 FilterConfig 对象里可以获取,即 filterConfig.getInitParameter("name"),但是初始化时获取的过滤器名字和设定的执行顺序 order 无关,而是根据默认的类加载顺序而来的。
2) 对于 frBean.setUrlPatterns(urls),可以设置多个 URL 匹配规则,setUrlPatterns 接收一个 List<String> 类型的参数。其实现代码在 WebFilterConfig.java 中注册 RequestTest2Filter.java 时已经给出相关示例。
3) 当不设置 setOrder 次序时,过滤器的执行顺序默认是 Bean 的加载顺序。在当前 WebConfig 类中,先加载 RequestTest1Filter.java 过滤器,后加载 RequestTest2Filter.java 过滤器。