在学习java的一些框架的时候,对web.xml的相关配置不太懂,所以搜索了一些Filter(一系列的过滤器)、FilterChain;FileterDispatcher(Filter的程序调度)、Intercepter(拦截器)一些相关的资料学习。在看到Struts 2的工作机制后,知道客户端的每一次请求,在到达Servlet之前,Filter会对请求做一系列的过滤。而Filter是根据web.xml中的配置怎么过滤客户端请求的;

 

1.Filter

Filter 有如下几个用处:

  • l  在HttpServletRequest 到达Servlet 之前,拦截客户的HttpServletRequest 。 
  • l  根据需要检查HttpServletRequest ,也可以修改HttpServletRequest 头和数据。 
  • l  在HttpServletResponse 到达客户端之前,拦截HttpServletResponse 。 
  • l  根据需要检查HttpServletResponse ,可以修改HttpServletResponse 头和数据。

Filter 有如下几个种类:

  • l  用户授权的Filter: Filter 负责检查用户请求,根据请求过滤用户非法请求。 
  • l  日志Filter: 详细记录某些特殊的用户请求。 
  • l  负责解码的Filter: 包括对非标准编码的请求解码。 
  • l  能改变XML 内容的XSLTFilter 等。 

创建一个Filter 只需两个步骤: 

  • (1)创建Filter 处理类;
  • (2)在web.xml 文件中配置Filter。

  创建Filter 必须实现javax.servlet.Filter 接口,在该接口中定义了三个方法。 

 

如下的代码实例中:设置了Filter的实现类(test.filter.LogFilter),用来执行过滤来自(/filter/*)这个路径下的请求;

 1 <!-- 定义Filter --> 
 2 <filter> 
 3     <!-- Filter 的名字 --> 
 4     <filter-name>log</filter-name> 
 5     <!-- Filter 的实现类 --> 
 6     <filter-class> test.filter.LogFilter</filter-class> 
 7 </filter> 
 8 <!-- 定义Filter 拦截地址 --> 
 9 <filter-mapping> 
10     <!-- Filter 的名字 --> 
11     <filter-name>log</filter-name> 
12     <!-- Filter 负责拦截的URL --> 
13     <url-pattern>/filter/*</url-pattern>
14 </filter-mapping> 

如下代码是test.filter.LogFilter的实现类;Filter的生命周期为:init()->doFilter()->destroy();在下面的请求Filter实现类处理中,仅在日志中记录请求的URL,对所有的请求都执行chain.doFilter(request,reponse)方法,当Filter 对请求过滤后,依然将请求发送到目的地址。 

 1 package test.filter;
 2  
 3 import javax.servlet.Filter;
 4 import javax.servlet.FilterChain;
 5 import javax.servlet.FilterConfig;
 6 import javax.servlet.ServletContext;
 7 import javax.servlet.ServletRequest;
 8 import javax.servlet.ServletResponse;
 9 import javax.servlet.http.HttpServletRequest;
10  
11 public class LogFilter implements Filter { 
12     private FilterConfig config; 
13     // 实现初始化方法 
14     public void init(FilterConfig config) { 
15         this.config = config; 
16     } 
17     // 实现销毁方法 
18     public void destroy() { 
19         this.config = null; 
20     } 
21     public void doFilter(ServletRequest request, ServletResponse response, 
22             FilterChain chain) { 
23         // 获取ServletContext 对象,用于记录日志 
24         ServletContext context = this.config.getServletContext(); 
25         long before = System.currentTimeMillis(); 
26         System.out.println("开始过滤... "); 
27         // 将请求转换成HttpServletRequest 请求 
28         HttpServletRequest hrequest = (HttpServletRequest) request; 
29         // 记录日志 
30         context.log("Filter已经截获到用户的请求的地址: " + hrequest.getServletPath()); 
31         try { 
32             // Filter 只是链式处理,请求依然转发到目的地址。 
33             chain.doFilter(request, response); 
34         } catch (Exception e) { 
35             e.printStackTrace(); 
36         } 
37         long after = System.currentTimeMillis(); 
38         // 记录日志 
39         context.log("过滤结束"); 
40         // 再次记录日志 
41         context.log(" 请求被定位到" + ((HttpServletRequest) request).getRequestURI() 
42                 + "所花的时间为: " + (after - before)); 
43     } 
44 } 

通过上述步骤的操作,此时就可以通过URI进行访问。具体访问后会在log文件中的localhost文件中产生具体的访问日志。如下所示:

2010-12-28 21:12:50 org.apache.catalina.core.ApplicationContext log
信息:  请求被定位到/examples/jsp/jsp2/el/basic-arithmetic.jsp所花的时间为: 0
2010-12-28 21:14:55 org.apache.catalina.core.ApplicationContext log
信息: Filter已经截获到用户的请求的地址: /jsp/jsp2/el/basic-comparisons.jsp
2010-12-28 21:14:56 org.apache.catalina.core.ApplicationContext log
信息: 过滤结束

相应地,我们还可以自定义"编码的修正"、"用户权限的认证"等的实现类。

但是,我们开发过程中会出现如下的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>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>*.shtml</url-pattern>
    </filter-mapping>

该Filter的拦截URL是对所有的.shtml请求的拦截;该Filter的实现类,是Spring框架中默认的实现类,这样就为我们节省了自定义实现类的精力。

 

2.FilterChain

上面提到的两个"编码的修正"和"用户权限的认证"的过滤器,前者(EncodingFilter)负责设置编码,后者(SecurityFilter)负责控制权限,服务器会按照web.xml中过滤器定义的先后循序组装成一条链,然后一次执行其中的doFilter()方法。

程序实现了doFilter()方法,实现该方法就可实现对用户请求进行预处理,也可实现对服务器响应进行后处理——它们的分界线为是否调用了chain.doFilter(),执行该方法之前,即对用户请求进行预处理;执行该方法之后,即对服务器响应进行后处理。

 

Filter的初始化时对<filter>标签设置的实现类的初始化;多个Filter初始化的顺序是Filter定义的先后顺序,而在一个客户端请求过来触发多个Filter时,Filter的调用是<filter—mapping>定义的先后顺序。FilterChain中的Filter执行遵循栈的设计思想,如Filter1有Filter1Before、Filter1After,Filter2有Filter2Before、Filter2After执行语句块,如果Filter的调用顺序是Filter1、Filter2;则语句的执行顺序为:

  • Filter1Before;
  • Filter2Before;
  • Filter2After;
  • Filter1After;

实际上Filter和Servlet极其相似,区别只是Filter不能直接对用户生成响应。实际上Filter里doFilter()方法里的代码就是从多个Servlet的service()方法里抽取的通用代码,通过使用Filter可以实现更好的复用。