
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中。