Filter 技术是servlet 2.3 新增加的功能。servlet2.3是sun公司于2000年10月发布的,它的开发者包括许多个人和公司团体,充分体现了sun公司所倡导的代码开放性原则。在众多参与者的共同努力下,servlet2.3比以往功能都强大了许多,而且性能也有了大幅提高。
Filter对象起到的过滤的任务,在请求一个资源(servlert或者是静态内容)或是从资源返回的时候使用。 设计Filter的一些实际应用例子如下:
1) Authentication Filters (身份认证)
2) Logging and Auditing Filters (日志及审计)
3) Image conversion Filters (图像转换)
4) Data compression Filters (数据压缩)
5) Encryption Filters (数据加密)
6) Tokenizing Filters
7) Filters that trigger resource access events
8) XSL/T filters
9) Mime-type chain Filter
filter最重要的功能,它使用户可以改变一个request和修改一个response. Filter 不是一个servlet,它不能产生一个response,它能够在一个request到达servlet之前对request进行预处理,也可以在 response离开servlet时处理response.换种说法,filter其实是一个“servlet chaining“(servlet 链).
所有定义的Filter,都必须实现javax.servlet.Filter接口。
需要实现的方法有三个,他们分别是:
1. void setFilterConfig(FilterConfig config) //设置filter 的配置对象;
2. FilterConfig getFilterConfig() //返回filter的配置对象;
3. void doFilter(ServletRequest req,ServletResponse res,FilterChain chain) //执行filter 的工作.
注:现setFilterConfig和getFilterConfig方法已取消,代之为init(FilterConfig config)和destory()方法。
init(FilterConfig filterConfig) |
doFilter(ServletRequest request, ServletResponse response, FilterChain chain) |
destroy() |
init方法只会初始化一次,就是在初始化的时候,以后有要处理的请求的话,该方法不会再执行
对于服务器的多次请求,doFilter,destroy这两个方法都会被调用。
FilterConfig接口有方法可以找到filter名字及初始化参数信息.服务器可以设置FilterConfig为空来指明filter已经终结.
每一个filter从doFilter()方法中得到当前的request及response.在这个方法里,可以进行任何的针对request及 response的操作.(包括收集数据,包装数据等).filter调用chain.doFilter()方法把控制权交给下一个filter.一个 filter在doFilter()方法中结束.如果一个filter想停止request处理而获得对response的完全的控制,那它可以不调用下 一个filter.
一个filter可以包装request 或response以改变几个方法和提供用户定制的属性.Api2.3提供了HttpServletRequestWrapper 和HttpServletResponseWrapper来实现.它们能分派最初的request和response.如果要改变一个方法的特性,必须继 承wapper和重写方法.下面是一段简单的日志filter用来记录所有request的持续时间,并且获取配置的filter参数及名字
package com.cn.greenroom.flter; import java.io.IOException; import java.util.Enumeration; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; public class FilterOne implements Filter { FilterConfig filterConfig ; public void init(FilterConfig filterConfig) throws ServletException { System.out.println("1 init >> "); this.filterConfig = filterConfig; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ServletContext context = filterConfig.getServletContext(); //获取配置的filter的filtername String servletname = filterConfig.getFilterName(); System.out.println("filerName:" + servletname); //获取该filter的配置参数 Enumeration en = filterConfig.getInitParameterNames(); while (en.hasMoreElements()) { String property = (String)en.nextElement(); Object value = filterConfig.getInitParameter(property); System.out.println("initParam:" + property + " " + value); } //获取请求的uri路径 HttpServletRequest req = (HttpServletRequest) request; System.out.println(req.getRequestURI()); //执行时间 long bef = System.currentTimeMillis(); chain.doFilter(request, response); System.out.println(System.currentTimeMillis()); long aft = System.currentTimeMillis(); context.log("Request to " + request.getLocalAddr() + ": " + (aft-bef)); } public void destroy() { System.out.println("1 destroy >> "); } }
只是有Fiter的定义还是不够的,还需要将其配置到web.xml中
<!-- 在web.xml中定义一个filter --> <filter> <filter-name>filterOne</filter-name> <filter-class>com.cn.greenroom.flter.FilterOne</filter-class> <init-param> <param-name>name</param-name> <param-value>value</param-value> </init-param> </filter> <!-- 定义该filer所起作用的url请求 --> <filter-mapping> <filter-name>filterOne</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
当web.xml中配置了过个filter的时候,如下所示的配置
<filter> <filter-name>filterOne</filter-name> <filter-class>com.cn.greenroom.flter.FilterOne</filter-class> <init-param> <param-name>name</param-name> <param-value>value</param-value> </init-param> </filter> <filter> <filter-name>filterTwo</filter-name> <filter-class>com.cn.greenroom.flter.FilterTwo</filter-class> </filter> <filter-mapping> <filter-name>filterTwo</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>filterOne</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
对于init方法,则其执行顺序则是 FilterOne.init() -> FilterTwo.init() ;(该顺序与filter配置的先后有关)
当有请求进入的时候,那么则有如下的过滤顺序(Filter执行的先后顺序与filter-mapping有关)
req -> FilterTwo.doFilter -> FilterOne.doFilter() -> FilterOne.destroy() -> FilterTwo.destroy() ;
对于中文系统的j2ee项目,经常碰到乱码的问题,常用的做法就是添加一个过滤器,然后将所有的请求都转换为utf-8编码一下是该过滤器的实例
①在web.xml中添加如下的配置
<filter> <filter-name>characterEncoding</filter-name> <filter-class>com.cn.greenroom.flter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
② CharacterEncodingFilter.java
package com.cn.greenroom.flter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class CharacterEncodingFilter implements Filter{ // encoding type protected String encoding = null; // filter config protected FilterConfig filterConfig = null; //whether ignore the encoding type protected boolean ignore = true; /** * destroy the filter */ public void destroy() { this.encoding = null; this.filterConfig = null; } /** * do filte */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // encoding logic if (ignore || (request.getCharacterEncoding() == null)) { String encoding = selectEncoding(request); if (encoding != null) { request.setCharacterEncoding(encoding); } else { } } // pass request,response to the next filter chain.doFilter(request, response); } /** * init filter */ public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; this.encoding = filterConfig.getInitParameter("encoding"); String value = filterConfig.getInitParameter("ignore"); if (value == null) this.ignore = true; else if (value.equalsIgnoreCase("true")) this.ignore = true; else if (value.equalsIgnoreCase("yes")) this.ignore = true; else this.ignore = false; } /** * Get ignore flag * @return */ public boolean getIgnore(){ return this.ignore; } /** * return the encoding type * @param request * @return */ public String selectEncoding(ServletRequest request) { return (this.encoding); } /** * Get filterConfig * @return */ public FilterConfig getFilterConfig(){ return this.filterConfig; } }
<!-- Filter 定义 --> <!-- Character Encoding filter --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
spring中是通过org.springframework.web.filter.CharacterEncodingFilter这个类来设置的。 Spring中初始化则是在其公共父类抽象类GenericFilterBean中 在GenericFilterBean中有一个静态的私有类FilterConfigPropertyValues 该类负责解析所有的配置初始化参数,其解析的具体方法如下:
Enumeration en = config.getInitParameterNames(); while (en.hasMoreElements()) { String property = (String)en.nextElement(); Object value = config.getInitParameter(property); addPropertyValue(new PropertyValue(property, value)); if (missingProps != null) { missingProps.remove(property); } }
CharacterEncodingFilter的两个父类
// CharacterEncodingFilter 的直接父类 OncePerRequestFilter public class CharacterEncodingFilter extends OncePerRequestFilter public abstract class OncePerRequestFilter extends GenericFilterBean public abstract class GenericFilterBean implements Filter, BeanNameAware, EnvironmentAware, ServletContextAware, InitializingBean, DisposableBean
CharacterEncodingFilter 的直接父类OncePerRequestFilter中有对doFilter的实现,而GenericFilterBean中则有对 init()以及destory的实现
在CharacterEncodingFilter中实际其作用的是doFilterInternal方法,该方法则来源于 OncePerRequestFilter中对doFilter的实现
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { if ((!(request instanceof HttpServletRequest)) || (!(response instanceof HttpServletResponse))) { throw new ServletException("OncePerRequestFilter just supports HTTP requests"); } HttpServletRequest httpRequest = (HttpServletRequest)request; HttpServletResponse httpResponse = (HttpServletResponse)response; String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName(); boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null; if ((hasAlreadyFilteredAttribute) || (skipDispatch(httpRequest)) || (shouldNotFilter(httpRequest))) { //对于已经调用过的filter直接进行转发操作 filterChain.doFilter(request, response); } else { //在进行filter之前,将filter的状态设置为已经过滤过,防止重复执行filter request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE); try { //该方法是在OncePerRequestFilter中是一个抽象方法,此处的设计应用了模板类的设计方式 //spring中的filter只需要对 doFilterInternal 进行实现即可 doFilterInternal(httpRequest, httpResponse, filterChain); } finally { request.removeAttribute(alreadyFilteredAttributeName); } } }
关于springFilter的详细解析,请参考:http://blog.csdn.net/kiss_vicente/article/details/7599186