Servlet API 很久以前就已成为企业应用开发的基石,而 Servlet 过滤器则是对 J2EE 家族的相对较新的补充。在 J2EE 探索者 系列文章的最后一篇中,作者 Kyle Gabhart 将向您介绍 Servlet 过滤器体系结构,定义过滤器的许多应用,并指导您完成典型过滤器实现的三个步骤。
清单 1 展示了一个非常简单的过滤器,它跟踪满足一个客户机的 Web 请求所花的大致时间。
清单 1. 一个过滤器类实现
import javax.servlet.*; import java.util.*; import java.io.*; public class TimeTrackFilter 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 { Date startTime, endTime; double totalTime; startTime = new Date(); // Forward the request to the next resource in the chain chain.doFilter(request, wrapper); // -- Process the response -- \\ // Calculate the difference between the start time and end time endTime = new Date(); totalTime = endTime.getTime() - startTime.getTime(); totalTime = totalTime / 1000; //Convert from milliseconds to seconds StringWriter sw = new StringWriter(); PrintWriter writer = new PrintWriter(sw); writer.println(); writer.println("==============="); writer.println("Total elapsed time is: " + totalTime + " seconds." ); writer.println("==============="); // Log the resulting string writer.flush(); filterConfig.getServletContext(). log(sw.getBuffer().toString()); } } |
这个过滤器的生命周期很简单,不管怎样,我们还是研究一下它吧:
初始化
当容器第一次加载该过滤器时,init()
方法将被调用。该类在这个方法中包含了一个指向 FilterConfig
对象的引用。我们的过滤器实际上并不需要这样做,因为其中没有使用初始化信息,这里只是出于演示的目的。
过滤
过滤器的大多数时间都消耗在这里。doFilter()
方法被容器调用,同时传入分别指向这个请求/响应链中的 ServletRequest
、ServletResponse
和 FilterChain
对象的引用。然后过滤器就有机会处理请求,将处理任务传递给链中的下一个资源(通过调用 FilterChain
对象引用上的 doFilter()
方法),之后在处理控制权返回该过滤器时处理响应。
析构
容器紧跟在垃圾收集之前调用 destroy()
方法,以便能够执行任何必需的清理代码。
2. 配置 Servlet 过滤器
过滤器通过 web.xml 文件中的两个 XML 标签来声明。<filter>
标签定义过滤器的名称,并且声明实现类和 init()
参数。<filter-mapping>
标签将过滤器与 servlet 或 URL 模式相关联。
清单 2 摘自一个 web.xml 文件,它展示了如何声明过滤器的包含关系:
清单 2. 在 web.xml 中声明一个过滤器
<filter> <filter-name>Page Request Timer</filter-name> <filter-class>TimeTrackFilter</filter-class> </filter> <filter-mapping> <filter-name>Page Request Timer</filter-name> <servlet-name>Main Servlet</servlet-name> </filter-mapping> <servlet> <servlet-name>Main Servlet</servlet-name> <servlet-class>MainServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Main Servlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> |
上面的代码示例声明了一个过滤器("Page Request Timer"),并把它映射到一个 servlet("Main Servlet")。然后为该 servlet 定义了一个映射,以便把每个请求(由通配符指定)都发送到该 servlet。这是控制器组件的典型映射声明。您应该注意这些声明的顺序,因为千万不能背离这些元素的顺序。