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"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<filter>
<filter-name>cors</filter-name>
<filter-class>com.zzuhkp.mvc.CorsFilter</filter-class>
<init-param>
<param-name>allowedMethods</param-name>
<param-value>GET,POST</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>cors</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
@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<Class<?>> 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");
}
}
WebApplicationInitializer 配置
Spring 3.1 版本利用了上述 Servlet 規范中 ServletContainerInitializer 的特性,提供了這個接口的實現 SpringServletContainerInitializer,並在實現中回調了 Spring 提供的 WebApplicationInitializer 接口。因此,Spring MVC 環境也可以直接實現 WebApplicationInitializer 來手動配置 Filter。注意:只需要實現接口,無需特定配置,Servlet 容器會把這個類告訴 SpringServletContainerInitializer。示例代碼如下:
public class CorsWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
FilterRegistration.Dynamic dynamic = servletContext.addFilter("cors", new CorsFilter());
dynamic.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
dynamic.setInitParameter("allowedMethods", "GET,POST");
}
}
Spring Bean 配置
除了普通 Spring MVC 環境下的配置,Spring Boot 環境中,Spring Boot 1.4 及之后版本下還可以直接將 Filter 注冊為 Bean,Filter Bean 將應用到所有的請求中。示例代碼如下:
@Component
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
}
FilterRegistrationBean 配置
FilterRegistrationBean 同樣是 Spring Boot 1.4 版本提出的一個新類型,這個類允許指定過濾的請求路徑,將這個類配置為 Bean 即可。示例代碼如下:
@Configuration
public class MvcConfig {
@Bean
public FilterRegistrationBean<CorsFilter> filterRegistrationBean() {
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter());
bean.addUrlPatterns("/*");
bean.addInitParameter("allowedMethods", "GET,POST");
return bean;
}
}
總結
本文主要對 Filter 的概念做了簡單介紹,並介紹了幾種 Spring MVC 內置的 Filter,最后介紹了 6 種在 Spring MVC 配置 Filter 的方式,其中 web.xml、@WebFilter、ServletContainerInitializer、WebApplicationInitializer 這 4 種配置方式適用於普通的 Spring MVC 環境,Filter Bean 配置、FilterRegistrationBean Bean 配置 適用於 Spring Boot 環境下的 Spring MVC,讀者需要加以留意。
參考: |