3.10 Filter

Filter(过滤器)是Servlet 2.3规范以后增加的新特性。Filter拦截请求和响应,以便查看、提取或以某种方式操作正在客户端和服务器之间交换的数据。Filter可以改变一个请求(Request)或者是修改响应(Response)。Filter与Servlet的关联由Web应用的配置描述文件或注解来明确。用户发送请求给Servlet时,在Servlet处理请求之前,与此Servlet关联的Filter首先执行,然后才是Servlet的执行,Servlet执行完毕又会回到Filter。如果一个Servlet有多个Filter,则根据配置的先后次序依次执行。

Filter主要用在以下几个方面:

(1)访问特定资源(Web页、JSP页、Servlet)时的身份验证。

(2)访问资源的记录跟踪。

(3)访问资源的转换。

一个Filter必须实现javax.Servlet.Filter接口,即实现下面的三个方法:

(1)doFilter(ServletRequest, ServletResponse, FilterChain)。用来实现过滤行为的方法。引入的FilterChain对象提供了后续Filter所要调用的信息。

(2)init(FilterConfig)。由容器所调用的Filter初始化方法。容器确保在第一次调用doFilter方法前调用此方法,一般用来获取在web.xml文件中指定的初始化参数。

(3)destroy()。容器在破坏Filter实例前,doFilter()中的所有活动都被该实例终止后,调用该方法。

下面演示如何利用Filter来记录Web组件对请求的响应时间。首先生成一个Filter。在“项目”视图中选中Web应用程序Chapter3,右击,在弹出的快捷菜单中选择“新建”→“文件/文件夹”命令,弹出“新建文件”对话框,如图3-37所示。

图3-37 创建Filter

在“类别”列表中选中Web,在“文件类型”列表中选中“过滤器”,单击“下一步”按钮,得到如图3-38所示的“New过滤器”对话框。

图3-38 Filter的名称和位置

如图3-38所示,在“类名”文本框中输入Filter的名称TimeTrackFilter,在“包”文本框中输入包的名称com.servlet,单击“下一步”按钮,得到如图3-39所示的对话框。

图3-39 配置Filter部署

在这里要配置过滤器的部署信息,即将过滤器与它要过滤的Web组件或URL模式关联起来。

在“过滤器名称”文本框中可以输入过滤器的逻辑名称,这里采用默认选项。单击“编辑”按钮打开“过滤器映射”对话框来设置过滤器映射信息,如图3-40所示。

图3-40 “过滤器映射”对话框

说明:过滤器有两种映射模式。一种是对URL模式的映射,这也是默认的映射模式。在URL模式中可以使用通配符号,如“/*”。另外一种模式是对Servlet的映射,这时过滤器关联的是Servlet的逻辑名称。

选中单选按钮URL,并在其右侧的文本框中输入“/Main”。单击“确定”按钮完成过滤器映射配置。此时Filter关联的URL对应的组件为3.9节创建的Servlet Main。最后单击图3-39中的“完成”按钮,Filter创建完毕。完整代码如程序3-33所示。

程序3-33:TimeTrackFilter.java

    …
    @WebFilter(filterName = "TimeTrackFilter", urlPatterns = {"/Main"})
    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;
          StringWriter sw = new StringWriter();
          System.out.println("我在Filter中");
          startTime = new Date();
          chain.doFilter(request, response);
          endTime = new Date();
          totalTime = endTime.getTime() - startTime.getTime();
          totalTime = totalTime ;
          System.out.println("我在Filter中");
          PrintWriter writer = new PrintWriter(sw);
          writer.println("===============");
          writer.println("耗时:" + totalTime + " 毫秒" );
          writer.println("===============");
          filterConfig.getServletContext(). log(sw.getBuffer().toString());
      }
    }

程序说明:跟Servlet一样,在新版本的Java EE规范中,提供了注解WebFilter来部署Filter组件,其中属性filterName为Filter的逻辑名称,属性urlPatterns为Filter的URL模式。

程序包含了所有Filter必须实现的3个接口方法:init、destroy和doFilter。当容器第一次加载该过滤器时,init方法将被调用。该类在这个方法中包含了一个指向FilterConfig对象的引用。对请求和响应的过滤功能主要由doFilter实现。Web容器在垃圾收集之前调用destroy方法,以便能够执行任何必需的清理代码。

TimeTrackFilter主要实现对过滤的Web组件处理耗时的跟踪,在调用FilterChain对象的doFilter(request, response)方法之前创建一个Date对象startTime, FilterChain对象在doFilter(request, response)方法执行完毕后,控制权仍旧回到当前的Filter,此时,再创建一个Date对象endTime来获取当前时刻,二者相减,就得到被过滤Web组件的执行时间。

为了使执行效果更明显,可以修改Servlet Main,使Servlet的线程暂时中止2秒。修改后的代码如程序3-34所示。

程序3-34:Main.java

    package com.servlet;
    …
    public class Main extends HttpServlet {
     protected void processRequest(HttpServletRequest request, HttpServletResponse
     response)
        throws ServletException, IOException{
          System.out.println("我在Servlet Main中");
          try{
          Thread.sleep(2000);
          }catch (InterruptedException ie){
              System.out.println(ie.toString());
          }
          String userID=request.getParameter("userID");
        …
        }

程序说明:斜体部分为新增的代码,即调用Thread对象的sleep方法使线程暂停2秒。

重新发布Web应用,打开IE浏览器,在地址栏中输入http://localhost:8080/Chapter3/dl.html,得到如图3-34所示的运行结果页面。在“用户名”文本框输入guest,在“密码”文本框输入guest,单击“提交”按钮,此时浏览器向URL模式“/Main”发出请求,由于URL模式“/Main”被关联到Filter TimeTrackFilter,则首先执行TimeTrackFilter的doFilter方法,然后执行Servlet Main,最后又回到Filter的doFilter方法。因此在Netbeans底部的“输出”窗口的GlassFish Server 3.1.1中可以看到如图3-41的输出信息。

图3-41 Filter运行过程中的输出信息

从上面的演示过程可以看出,开发人员不应当把Filter看作是请求到达Servlet之前的一道防火墙,而应当把它看作是包裹在Servlet组件外面的一层防护网。在请求到达Servlet前后都会经过Filter的处理。

Filter不仅可以对URL模式进行过滤,还可以对Servlet组件的逻辑名称进行过滤。下面为TimeTrackFilter添加对Servlet组件PDFServlet的过滤。打开web.xml,在编辑器中选中“过滤器”视图,得到如图3-42所示的运行界面。

图3-42 修改Filter配置信息

单击“过滤器映射”下的“添加”按钮,弹出“编辑过滤器映射”对话框,如图3-43所示。

图3-43 修改Filter配置信息

选中“Servlet名称”单选按钮,在其右边的下拉列表中选中要过滤的Servlet的名称PDFServlet,单击“确定”按钮,完成TimeTrackFilter对Servlet组件PDFServlet的过滤设置。

重新发布Web应用,打开IE浏览器,在地址栏中输入http://localhost:8080/Chapter3/pdfshow,在Netbeans底部的“输出”窗口的GlassFish Server 3.1.1视图中可以看到执行Servlet组件PDFServlet的耗时信息。

在Servlet 2.4以上的Web容器中,过滤器可以根据请求分发器(request dispatcher)所使用的方法有条件地对Web请求进行过滤。在图3-43中,在“分发程序类型”组中可以看到四个检查框,分别代表以下分发类型:

·REQUEST——只有当request直接来自客户,过滤器才生效。

·FORWARD——只有当request被一个请求分发器使用forward方法转到另一个Web组件时,过滤器才生效。

·INCLUDE——只有当request被一个请求分发器使用include方法转到一个Web构件时,过滤器才生效。

·ERROR——只有当request被一个请求分发器使用“错误信息页”机制方法转到一个Web组件时,过滤器才生效。

以上四个条件可以组合使用。

在图3-39中,选中Filter的映射项PDFServlet,单击“编辑”按钮来修改映射信息。在弹出的“编辑过滤器映射”对话框中,选中FORWARD复选框,如图3-44所示。单击“确定”按钮,完成对过滤器映射信息的修改。

图3-44 修改Filter映射信息

重新发布Web应用,打开IE浏览器,在地址栏中输入http://localhost:8080/Chapter3/pdfshow,在Netbeans底部的“输出”窗口的GlassFish Server 3.1.1视图中将看不到Filter输出的执行Servlet组件PDFServlet的耗时信息。因为按照修改后的Filter映射设置,只有调用请求分发器的forward方法对Servlet组件PDFServlet发送请求才被过滤器过滤。