搞定J2EE核心技术与企业应用
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

5.3 Filter技术

虽然Filter技术是Servlet 2.3增加的功能,但是Servlet 3.0也加强了对该技术的支持。Servlet 3.0是Sun公司于2010年发布的,它的开发者包括许多个人和公司团体,充分体现了Sun公司所倡导的代码开放性原则。由于众多参与者的共同努力,Servlet 3.0的功能强大了很多,而且性能也有了大幅度提高。

5.3.1 Filter技术原理

Servlet 3.0使得开发人员实现如下目标:

● 简单性。

● 减轻开发工作量。

● 遵循Web 2.0原则。

为了使开发过程更加轻松,Servlet 3.0引入了注释。类似于EJB 3.1的改变,注释的引入使得Web部署描述符web.xml成为可选项。

同时该版本Servlet也支持Filter功能,因为Filter可以使用户增强request和response对象。Filter虽然不像Servlet一样,能够生成request和response对象,但是其能够在一个request到达Servlet之前预处理request,也可以在离开Servlet时处理response。

一个Filter包括:

● 在Servlet被调用之前截获。

● 在Servlet被调用之前检查Servlet的request。

● 根据需要修改request头和request数据。

● 根据需要修改response头和response数据。

● 在Servlet被调用之后截获。

在Filter的最初版本中,一个Filter必须实现javax.servlet.Filter接口并定义3个方法:

● void setFilterConfig(FilterConfig config),设置Filter的配置对象。

● FilterConfig getFilterConfig(),返回Filter的配置对象。

● void doFilter(ServletRequest req, ServletResponse res, FilterChain chain),执行Filter的工作。

服务器每次只调用setFilterConfig方法一次,准备Filter的处理;调用doFilter方法多次,以处理不同的请求。每一个Filter从doFilter()方法中得到当前的request及response。在这个方法中,可以针对request及response进行任何的操作,Filter调用chain.doFilter()方法把控制权交给下一个Filter。一个Filter在doFilter()方法中结束。如果一个Filter想停止request处理而获得对response的完全控制,那么它可以不调用下一个Filter。

而在Filter的最新版本中,只需要实现javax.servlet.Filter接口并定义两个方法即可:

● void init(FilterConfig config),进行Filter的初始化工作。

● void doFilter(ServletRequest req, ServletResponse res, FilterChain chain),执行Filter的工作。

5.3.2 Filter示例

一个Filter可以包装request或response以改变多个方法和提供用户定制的属性。Servlet 2.3提供了HttpServletRequestWrapper和HttpServletResponseWrapper来实现,它们能分派最初的request和response,如果要改变一个方法的特性,则必须继承wapper和重写方法。下面是一段简单的日志Filter,用来记录所有request的持续时间。

      public class LogFilter implements Filter {
          FilterConfig config;
          public void setFilterConfig(FilterConfig config) {
              this.config = config;
          }
          public FilterConfig getFilterConfig() {
              return config;
          }
          public void doFilter(ServletRequest req,
          ServletResponse res,
          FilterChain chain) {
              //获取环境上下文
              ServletContext context = getFilterConfig().getServletContext();
              //记录开始时间
              long bef = System.currentTimeMillis();
              //传递给下一个Filter或Servlet
              chain.doFilter(req, res);
              //记录执行完的时间
              long aft = System.currentTimeMillis();
              context.log("Request to " + req.getRequestURI()
              + ": " + (aft-bef));
          }
      }

当服务器端调用setFilterConfig()时,Filter保存config信息,在doFilter()方法中通过config信息得到servletContext。如果要运行这个Filter,则必须配置到web.xml文件中。

          <filter>
          <filter-name> log </filter-name> //定义过滤器的名字
          <filter-class> com.gc.log.LogFilter </filter-class> //定义过滤器类
          </filter>
          <filter-mapping>
          <filter-name>log</filter-name>
          <servlet-name>servletname</servlet-name> //定义要过滤的内容
          </filter-mapping>
          <servlet>
          <servlet-name>servletname</servlet-name> //定义Servlet的名称
          <servlet-class> com.gc.action.servletclass</servlet-class> //定义Servlet的类
          </servlet>
          <servlet-mapping>
          <servlet-name>servletname</servlet-name>
          <url-pattern>*</url-pattern> //定义要访问拦截的URL
          </servlet-mapping>

把这个web.xml文件放到WEB-INF下,当每次请求一个request时,先到LogFilter中去并调用doFilter()方法,然后才到各自的Servlet中。