在学习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可以实现更好的复用。