0%

Servlet filter记录

在 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,其作用可见如下。

  1. 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”。

  1. HiddenHttpMethodFilter

form 表单只能使用 GET 和 POST 方法,这一个过滤器可以包装为 PUT、DELETE 和 PATCH 方法。

使用时,需要在 form 表单中指定 method 为 POST,同时添加一个 key 为 “_method” 的属性,指定为实际要使用的方法(PUT、DELETE 或 PATCH),HiddenHttpMethodFilter 即可包装为该 method。这一属性默认 key 为”_method”,也可以指定为其他值。

另,文档提示,这一 filter 要在 multipart 处理过后执行,所以如果自定义了 MultipartFilter,MultipartFilter 应指定在 HiddenHttpMethodFilter 前执行。

  1. FormContentFilter

对 PUT, PATCH 和 DELETE 的方法传递的参数进行包装,使得可以像 POST 请求一样,可以作为 Servlet request parameters 来读取。

其他常用 filter

以后补充这部分吧…

Spring Boot 自定义 filter

@WebFilter 注解

是 Servlet 3 中定义的注解,用于声明一个 Servlet filter,urlPatterns 属性可指定匹配的 url 的列表。使用时需要添加 @ServletComponentScan 才会生效,@ServletComponentScan 需要指定扫描的包或者类,可以直接添加在 @SpringBootApplication 类之上使扫描生效。

实现 Filter 接口添加一个 filter

  1. 使用 @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
2
3
4
5
6
7
8
9
@WebFilter
public class MyFilter implements Filter {

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("MyFilter");
filterChain.doFilter(servletRequest, servletResponse);
}
}

要记得调用 filterChain.doFilter() 方法,来“Causes the next filter in the chain to be invoked”。

这一方法无法指定顺序(实现 OrderedFilter 接口也不能,?)。

  1. 使用 @Component 声明,实现 Filter 接口,并使用 @Order 指定顺序

不需要指定 @WebFilter 中的属性时,也可以使用 @Component 来声明以注册一个新的 filter。

1
2
3
4
5
6
7
8
9
10
11
@Order(-9901)
@Component
public class MyFilter implements Filter {

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("MyFilter");
filterChain.doFilter(servletRequest, servletResponse);
}
}

如上,将添加一个 MyFilter,并且其执行顺序在默认的 FormContentFilter 之前(FormContentFilter 默认 order 为 -9900)。

  1. 使用 @Component 声明,实现 OrderedFilter 接口指定顺序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class MyFilter implements OrderedFilter {

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("MyFilter");
filterChain.doFilter(servletRequest, servletResponse);
}

@Override
public int getOrder() {
return -9901;
}
}

如上,将添加一个 MyFilter,并且其执行顺序在默认的 FormContentFilter 之前(FormContentFilter 默认 order 为 -9900),效果与使用 @Order(-9901) 相同。

继承某个 ***Filter 类来替换默认实现或者添加一个 filter

以继承 FormContentFilter 为例。

  1. 使用 @WebFilter 声明,继承 FormContentFilter

代码如下:

1
2
3
4
5
6
7
8
9
10
@WebFilter
public class MyFilter extends FormContentFilter {

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println("MyFilter");
filterChain.doFilter(servletRequest, servletResponse);
}

}

与“使用 @WebFilter 声明,实现 Filter 接口”类似,新注册一个 FormContentFilter,无法指定顺序。同样需要在 @SpringBootApplication 类下添加 @ServletComponentScan 使之生效。

  1. 使用 @Component 声明,继承 FormContentFilter,并使用 @Order 指定顺序

对如下代码,MyFilter 将直接作为一个 bean,替换掉 Spring Boot 中 FormContentFilter 的默认实现的 bean。

1
2
3
4
5
6
7
8
9
@Component
public class MyFilter extends FormContentFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println("MyFilter");
filterChain.doFilter(request, response);
}
}

只使用 @Component 声明一个继承自 FormContentFilter 的类,此自定义 filter 将替换掉默认的 FormContentFilter,但是顺序是默认顺序,而不是原 FormContentFilter 的顺序(查看 Spring Boot 的 WebMvcAutoConfiguration 类可以看到,原 FormContentFilter 是实例化了一个 OrderedFormContentFilter,order 为 -9900)。

如果使用 @Order 来指定顺序,如下:

1
2
3
4
5
6
7
8
9
10
11
@Order(-9901)
@Component
public class MyFilter extends FormContentFilter {

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println("MyFilter");
filterChain.doFilter(request, response);
}
}

可以看到 @Order(-9901) 生效,MyFilter 按照 -9901 的值应有的顺序放到 filterChain 中。

自定义多个 filter 时的执行顺序

使用 @WebFilter 声明的 filter,可以指定 urlPatterns,将作为一个新的 filter 加入到 filterChain 中,无法指定其执行顺序,需要使用 @ServletComponentScan 扫描使之生效。

使用 @Component 注册的 filter,可以使用 @Order 指定顺序或者实现 OrderedFilter 接口指定顺序。

此外,还可以通过在代码中定义 bean 的方式,注册一个 FilterRegistrationBean,来定义一个 filter。