在 Spring Boot 中观察了一下有新的请求时,Servlet filter 的执行情况,做下记录。
Spring Boot 默认 Servlet filter
Spring Boot 会按照 ApplicationFilterChain 中的数组 filters 里保存的 filter 按顺序执行,对于 OrderedFilter 可以指定 order 值(一个 int 值),值小的先执行,不指定 order 时按照首字母顺序执行。
默认情况下,有 CharacterEncodingFilter、HiddenHttpMethodFilter、FormContentFilter 和 RequestContextFilter,以及 webSocket 的 WsFilter。CharacterEncodingFilter、HiddenHttpMethodFilter、FormContentFilter 和 RequestContextFilter 都是使用了 Ordered***Filter 实例,order 值分别是 -2147483648、-10000、-9900 和 -105。
RequestContextFilter 主要用于使用第三方 Servlet 时的情况。关于 CharacterEncodingFilter、HiddenHttpMethodFilter 和 FormContentFilter,其作用可见如下。
- CharacterEncodingFilter
用于指定 request 的编码方式。如果 request 没有指定编码方式,或者是 CharacterEncodingFilter 指定了 forceEncoding = true,会添加(覆盖)request 的编码方式,具体来说,其操作是在 header 的 Content-Type 中添加 charset 项。
同时,如果指定了 forceEncoding = true,也会在 response 的 header 的 Content-Type 中添加 charset 项,虽然对这一操作,文档提示”although this will usually be overridden by a full content type set in the view”。
- HiddenHttpMethodFilter
form 表单只能使用 GET 和 POST 方法,这一个过滤器可以包装为 PUT、DELETE 和 PATCH 方法。
使用时,需要在 form 表单中指定 method 为 POST,同时添加一个 key 为 “_method” 的属性,指定为实际要使用的方法(PUT、DELETE 或 PATCH),HiddenHttpMethodFilter 即可包装为该 method。这一属性默认 key 为”_method”,也可以指定为其他值。
另,文档提示,这一 filter 要在 multipart 处理过后执行,所以如果自定义了 MultipartFilter,MultipartFilter 应指定在 HiddenHttpMethodFilter 前执行。
- FormContentFilter
对 PUT, PATCH 和 DELETE 的方法传递的参数进行包装,使得可以像 POST 请求一样,可以作为 Servlet request parameters 来读取。
其他常用 filter
以后补充这部分吧…
Spring Boot 自定义 filter
@WebFilter 注解
是 Servlet 3 中定义的注解,用于声明一个 Servlet filter,urlPatterns 属性可指定匹配的 url 的列表。使用时需要添加 @ServletComponentScan 才会生效,@ServletComponentScan 需要指定扫描的包或者类,可以直接添加在 @SpringBootApplication 类之上使扫描生效。
实现 Filter 接口添加一个 filter
- 使用 @WebFilter 声明,实现 Filter 接口
以下代码将新注册一个 filter,同时需要在 @SpringBootApplication 类下添加 @ServletComponentScan 使之生效。
@WebFilter 注解的文档提醒,”At least one URL pattern MUST be declared in either the value or urlPattern attribute of the annotation, but not both”。
1 |
|
要记得调用 filterChain.doFilter() 方法,来“Causes the next filter in the chain to be invoked”。
这一方法无法指定顺序(实现 OrderedFilter 接口也不能,?)。
- 使用 @Component 声明,实现 Filter 接口,并使用 @Order 指定顺序
不需要指定 @WebFilter 中的属性时,也可以使用 @Component 来声明以注册一个新的 filter。
1 |
|
如上,将添加一个 MyFilter,并且其执行顺序在默认的 FormContentFilter 之前(FormContentFilter 默认 order 为 -9900)。
- 使用 @Component 声明,实现 OrderedFilter 接口指定顺序
1 |
|
如上,将添加一个 MyFilter,并且其执行顺序在默认的 FormContentFilter 之前(FormContentFilter 默认 order 为 -9900),效果与使用 @Order(-9901) 相同。
继承某个 ***Filter 类来替换默认实现或者添加一个 filter
以继承 FormContentFilter 为例。
- 使用 @WebFilter 声明,继承 FormContentFilter
代码如下:
1 |
|
与“使用 @WebFilter 声明,实现 Filter 接口”类似,新注册一个 FormContentFilter,无法指定顺序。同样需要在 @SpringBootApplication 类下添加 @ServletComponentScan 使之生效。
- 使用 @Component 声明,继承 FormContentFilter,并使用 @Order 指定顺序
对如下代码,MyFilter 将直接作为一个 bean,替换掉 Spring Boot 中 FormContentFilter 的默认实现的 bean。
1 |
|
只使用 @Component 声明一个继承自 FormContentFilter 的类,此自定义 filter 将替换掉默认的 FormContentFilter,但是顺序是默认顺序,而不是原 FormContentFilter 的顺序(查看 Spring Boot 的 WebMvcAutoConfiguration 类可以看到,原 FormContentFilter 是实例化了一个 OrderedFormContentFilter,order 为 -9900)。
如果使用 @Order 来指定顺序,如下:
1 |
|
可以看到 @Order(-9901) 生效,MyFilter 按照 -9901 的值应有的顺序放到 filterChain 中。
自定义多个 filter 时的执行顺序
使用 @WebFilter 声明的 filter,可以指定 urlPatterns,将作为一个新的 filter 加入到 filterChain 中,无法指定其执行顺序,需要使用 @ServletComponentScan 扫描使之生效。
使用 @Component 注册的 filter,可以使用 @Order 指定顺序或者实现 OrderedFilter 接口指定顺序。
此外,还可以通过在代码中定义 bean 的方式,注册一个 FilterRegistrationBean,来定义一个 filter。