和Servlet
一样,Filter
也是J2EE
中的一项规范、一个接口(基于Servlet 4.0.1
):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package javax.servlet;
import java.io.IOException;
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
}
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;
default void destroy() {
}
}
Filter
接口的源码很简洁,一共就三个方法,init()
、doFilter()
、destroy()
。
生命周期
Filter
的生命周期分为三个阶段:
- 初始化阶段:
init()
方法,由Web
容器在启动时完成实例化及初始化操作,在生命周期中只会调用一次; - 调用阶段:
doFilter()
方法,请求的位置处于Filter
的负责范围之内时,调用该方法; - 销毁阶段:
destroy()
方法,释放资源,销毁实例。
工作原理
Filter
的工作原理主要包括以下两条:
- 在用户的请求访问后端资源之前,拦截请求并做处理;
- 在服务器的响应返回给用户之前,对响应做一些处理。
当用户将请求发送到服务器后,如果没有配置Filter
的话,就会调用Servlet
的service()
方法,但是一旦给这个(些)Servlet
配置了Filter
之后,就会在调用service()
之前先调用Filter
的doFilter()
方法对请求做一些处理。
Filter
对服务器响应的处理也是在doFilter()
中进行的,具体见下文。
配置
<!-- filter 和 filter-mapping 必须成对出现才有效 -->
<!-- filter-name 可以自己取 -->
<!-- 同一个 Filter 的 filter 和 filter-mapping 中的 filter-name 必须相同 -->
<!-- filter-class 是 Filter 的完全限定名 -->
<!-- init-param 用于为过滤器指定初始化参数,
它的子元素param-name指定参数的名字,param-value指定参数的值 -->
<!-- async-supported 启用异步支持 -->
<filter>
<filter-name>FilterTest</filter-name>
<filter-class>com.vingyun.FilterTest</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<async-supported>true</async-supported>
</filter>
<!-- servlet-name 配置需要过滤操作的 Servlet 的 servlet-name -->
<!-- url-pattern 配置需要过滤操作的路径 -->
<!-- url-pattern 和 servlet-name 可以同时使用 -->
<!-- dispatcher 设置当以什么方式访问资源时进行过滤:ASYNC、REQUEST、ERROR、FORWARD、INCLUDE -->
<!-- ASYNC:异步方式,Servlet 3.0 新加特性,
需要将 filter 标签的 async-supported 子标签设置为 true,
同时需要将受该 Filter 影响的 Servlet 的 servlet 标签的 async-supported 子标签设置为 true -->
<!-- REQUEST:当用户直接访问页面时,Web容器将会调用过滤器,
如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用 -->
<!-- INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用,
除此之外,该过滤器不会被调用 -->
<!-- FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,
除此之外,该过滤器不会被调用 -->
<!-- ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用,
除此之外,过滤器不会被调用。 -->
<filter-mapping>
<filter-name>FilterTest</filter-name>
<servlet-name>Servlet</servlet-name>
<url-pattern>/test/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
多个Filter对一时的工作顺序
如果对于一个Servlet
或是一个路径配置多个Filter
(即Filter
链),那么将会按照这些Filter
的<filter-mapping>
标签的配置顺序进行过滤操作。
服务器响应过滤
上文说到Filter
可以过滤服务器响应的内容,但是在配置<dispatcher>
标签的时候并没有RESPONSE
这个属性,那么我们该怎么过滤服务器的响应呢?
在新建的Filter
中的doFilter()
方法中有这么一个调用方法:chain.doFilter(request, response);
:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws ServletException, IOException {
chain.doFilter(request, response);
}
如果没有,那我们可以自己加上,这个方法的用途下面再说,但是现在我们可以在这个方法之后使用response
对象来对响应内容进行操作。
chain.doFilter(request, response)
doFilter()
方法中的chain.doFilter(request, response)
方法的作用是将请求转发给Filter
链上的下一个Filter
,如果没有下一个Filter
,那就直接转发给被请求的资源。