Java Servlet 2.3版本引入了一种新的组件:Filter。Filter动态地转换和使用request和response中的信息。Filters一般来说并不是直接产生response,而是提供一种通用的功能,用于附加到servlet or JSP页面中。
这些API都在javax.servlet
包下面,包括Filter
, FilterChain
, FilterConfig
接口。
通过实现Filter接口就可以定义一个Filter。
You define a filter by implementing the Filter interface.
FilterChain是通过WEB容器(比如tomcat)传递到Filter中的,它提供了一种调用一系列Filter的机制。
A filter chain, passed to a filter by the container, provides a mechanism for invoking a series of filters.
FilterConfig包含了一些初始化的数据(可能为null)。
A filter config contains initialization data.
Filter接口最重要的方法就是doFilter
方法,这是它的核心。
通常具有以下操作:
参考:
https://www.oracle.com/technetwork/java/filters-137243.html
除了doFilter之外,还必须实现init和destroy方法。其中init方法是当Filter初始化的时候,由容器调用的,用于传递一些数据。
比如下面这个不改变任何request和response的Filter,只是用于记录Servlet 的访问次数:
public final class HitCounterFilter implements Filter {
private FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig)
throws ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (filterConfig == null)
return;
StringWriter sw = new StringWriter();
PrintWriter writer = new PrintWriter(sw);
Counter counter = (Counter)filterConfig.
getServletContext().
getAttribute("hitCounter");
writer.println();
writer.println("===============");
writer.println("The number of hits is: " +
counter.incCounter());
writer.println("===============");
// Log the resulting string
writer.flush();
filterConfig.getServletContext().
log(sw.getBuffer().toString());
...
chain.doFilter(request, wrapper);
...
}
}
再比如下面这个例子,修改了request中的字符编码(如果请求中没有指定Content-Type的字符编码,则容器会使用自己的字符编码),当然也可以修改Accept-Language、User-Agent,以及一些存储在当前用户session中的变量。
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain) throws
IOException, ServletException {
String encoding = selectEncoding(request);
if (encoding != null)
request.setCharacterEncoding(encoding);
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws
ServletException {
this.filterConfig = filterConfig;
this.encoding = filterConfig.getInitParameter("encoding");
}
protected String selectEncoding(ServletRequest request) {
return (this.encoding);
}
。。。
使用
元素在web.xml中声明,然后给它起一个名字:
,指定Filter的实现类
;以及一些初始化的参数
:
<filter>
<filter-name>Compression Filterfilter-name>
<filter-class>CompressionFilterfilter-class>
<init-param>
<param-name>compressionThresholdparam-name>
<param-value>10param-value>
init-param>
filter>
<filter-mapping>
<filter-name>Compression Filterfilter-name>
<servlet-name>CompressionTestservlet-name>
filter-mapping>
<servlet>
<servlet-name>CompressionTestservlet-name>
<servlet-class>CompressionTestservlet-class>
servlet>
<servlet-mapping>
<servlet-name>CompressionTestservlet-name>
<url-pattern>/CompressionTesturl-pattern>
servlet-mapping>
注意:这些元素必须要以这种顺序!
可以将一个Filter对应到多个Servlet中,也可以将多个Filter对应到一个Servlet中。
#TODO
Figure 1 Filter to Servlet Mapping
Recall that a filter chain is one of the objects passed to the doFilter method of a filter. This chain is formed indirectly via filter mappings. The order of the filters in the chain is the same as the order that filter mappings appear in the web application deployment descriptor.
When a URL is mapped to servlet S1, the web container invokes the doFilter method of F1. The doFilter method of each filter in S1’s filter chain is invoked by the preceding filter in the chain via the chain.doFilter method. Since servlet S1’s filter chain contains filters F1 and F3, F1’s call to chain.doFilter invokes the doFilter method of filter F3. When F3’s doFilter method completes, control returns to F1’s doFilter method.
The deployment descriptor just discussed puts the hit counter and XSLT filter in the filter chain of FilteredFileServlet. The hit counter filter logs access whenever FilteredFileServlet is invoked, but inserts the value of the counter into the response after the XSLT transformation only if the response type is HTML: