「聊一聊Spring MVC」Filter 配置的 6 种方式


Filter简介

过滤器 Filter 在 Servlet 2.3 版本中被首次提出,唯一的作用就是过滤,它不仅可以过滤请求,还可以过滤响应,当请求到达 Servlet 容器,会先经过 Filter ,然后再交给 Servlet,之后 Filter 还可以对 Servlet 的响应进一步处理。并且多个 Filter 还能形成一个链。使用图示表达如下。

??¨è??é???????¥?????????è?°

Filter 的特性使得 Filter 可以对请求或响应进行包装,修改请求头、请求体、响应头、响应体。由于请求先到达 Filter,Filter 还可以做一些全局性的工作,例如日志打印、登录校验等等。

Filter 生命周期

Filter 作为一个接口,具体实现由用户负责,先看这个接口的定义:

public interface Filter {

    public void init(FilterConfig filterConfig) throws ServletException;

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;

    public void destroy();
}

Filter 中定义了三个方法:

  • init:这是 Filter 的初始化方法,这个方法只会被容器调用一次,方法参数 FilterConfig 表示 Filter 的配置,可以利用这个参数读取初始化参数、ServletContext 等。
  • doFilter:这是 Filter 处理请求的核心方法,当请求到达时容器先回调这个方法处理请求,除了 request、response,这个方法还可以拿到过滤器链 FilterChain 对象,只有调用了 FilterChain#doFilter方法,容器才会使用过滤器链中的下一个过滤器处理请求,如果当前 Filter 已经是链中的最后一个,则会交给 Servlet 处理。
  • destroy:容器停止时回调的方法,用于做一些资源清理的工作。

Spring MVC 内置 Filter

针对一些通用的场景,Spring MVC 内置了一些 Filter,下面看常用的有哪些:

  • CharacterEncodingFilter:用于设置请求体、响应体字符集的过滤器,使用这个过滤器可以统一字符编码,避免出现乱码现象。
  • CorsFilter:这是用来处理跨域的过滤器,请求到达这个过滤器时,会根据配置添加跨域相关的响应头。
  • FormContentFilter:对于请求方法为PUT、PATCH、DELETE,内容类型为表单application/x-www-form-urlencoded的请求,请求体中的参数无法通过 ServletRequest#getParameter 方法读取,这个过滤器对请求已经包装,以便可以通过 #getParameter 方法读取参数。

Spring MVC 中的 Filter 配置

自从 Spring MVC 提供拦截器 HandlerInterceptor 之后,过滤器 Filter 的一部分功能已经可以搬到拦截器了,但有时还是会不可避免的使用到过滤器,如跨域处理。因此需要自定义过滤器 Filter,并配置到 Servlet 容器中,Spring MVC 在不同的阶段也提供了不同的配置方案,具体来说主要有 6 种。

配置文件 web.xml 配置

Spring MVC 基于 Servlet 规范,Spring 早期,Servlet 和 Filter 配置方式与传统的 Java Web 项目并没有任何区别,需要在 web.xml 配置 Filter 清单。示例如下:

<?xml version="1.0" encoding="UTF-8"?>


    
        cors
        com.zzuhkp.mvc.CorsFilter
        
            allowedMethods
            GET,POST
        
    

    
        cors
        /*
    


@WebFilter 注解配置

Java 5 注解诞生后,Servlet 在 3.0 新引入了 @WebFilter 注解,用来替代 web.xml 文件中 Filter 的配置。Servlet 容器启动后会扫描类路径下的文件,遇到携带 @WebFilter 的注解后就会将这个类注册到容器中。因此在 Spring MVC 环境下也可以直接使用这个注解,和 xml 配置等同的注解配置如下:

@WebFilter(urlPatterns = "/*", initParams = {@WebInitParam(name = "allowedMethods", value = "GET,POST")})
public class CorsFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        chain.doFilter(request, response);
    }
}

ServletContainerInitializer 配置

除了常规的 Servlet 规范中的 xml 和 @WebFilter 配置方式, Servlet 3.0 规范还提供了一个 ServletContainerInitializer 接口,Servlet 容器启动后会扫描类路径,标注了 @HandlesTypes 注解的 ServletContainerInitializer 接口实现将会被回调。因此,在 Spring MVC 中也可以利用这个特性添加 Filter,具体代码如下:

@HandlesTypes({})
public class FilterInitializer implements ServletContainerInitializer {

    @Override
    public void onStartup(Set> c, ServletContext ctx) throws ServletException {
        FilterRegistration.Dynamic dynamic = ctx.addFilter("cors", new CorsFilter());
        dynamic.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
        dynamic.setInitParameter("allowedMethods","GET,POST");
    }
}