Filters are Java components that allow on the fly transformations of payload and header information in both the request into a resource and the response from a resource.
这一节描述了Java Servlet v2.4 API中的一部分classes和methods,它们为filter静态和动态的内容提供了一个轻量级的框架。这一节也描述了如何在web应用中配置filters,filter的使用惯例以及它们的实现的语法。
SRV.14提供了servlet filters的API文档。Filter的配置语法由部署描述符给出,这会在SRV13 “Deployment Descriptor” 中描述。在阅读本章时,读者应该把这些资源作为参考。
Filter是一种可反复使用的的代码,它可以传送HTTP request、response的内容,以及header信息。Filters不创建response或者是向servlets那样响应request,但它们可以为request修改或适配资源,还可以为response修改或适配资源。
Filters可以作用于动态或静态内容。作为本章节来说,提到的动态和静态内容指的是web资源。
Filter模型的主要概念都将在本节讨论。
应用程序开发者可以通过实现javax.servlet.Filter接口来创建一个filter,并且要提供一个公共的无参构造方法。Filter的class文件与其他组成web应用程序的静态内容和其他servlet的class一起打包到Web Archive(war)中。在部署描述符中,filter通过<filter>元素来声明,filter或filters集合可以通过定义<filter-mapping>元素被调用。调用通过把filter映射到一个特定的servlet(通过servlet的逻辑名称)来实现。或者通过URL pattern把filter映射到到一组servlet或者静态内容资源上。
在部署了web application之后,request致使container去访问web资源之前,container必须设置好filter列表,这些filter必须像下面描述的那样作用于web resources。container必须保证自身已经把filter列表每一个filter按照恰当的class实例化filter,并且调用它的init(FilterConfig config)方法。filter会通过抛出异常来表明它没有正确地运行。如果异常的类型是UnavaliableException,container可以检查exception的isPermanent属性,并且可以选择在下面的某个时间重新尝试实例化。
部署描述符中声明的filter,在container的每一个JVM中,一个<filter>标签只对应一个实例。Container来提供声明在filter的部署描述符中的filter配置,web application的ServletContext的引用,以及初始化参数集合。
当container接收到request时,container从filter列表中取出第一个filter实例,调用filter的doFilter方法,将参数ServletRequest,ServletResponse和container使用的FilterChain对象的引用传递进去。
典型的情况下,filter的doFilter方法会被实现以下全部或部分功能模式:
过滤的核心理念是为了包装request或是response以便可以override行为来执行过滤任务。在这个模型中,开发者不仅有能力在request,response对象上override已有的方法,也可以给某个filter或接下来的filter chain中的目标web资源提供适合某个特定过滤任务的新的API。例如,开发者想用高级output对象继承response对象,这种对象是output stream或者writer,就像某种API,它允许将DOM对象回写至client。
为了支持这种风格的filter,container必须达到下面的要求。当filter在container filter chain的实现中调用doFilter方法时,container必须保证它传递给filter chain中下一个实体的request对象和response对象和当前调用的filter传递进doFilter方法的是同一个对象。
当调用者包装request对象或response对象时,包装对象也有相同的要求作用于来自servlet or a filter to RequestDispatcher.forward or RequestDispatcher.include的调用。在这种情况下,被调用的servlet接收到的request和response对象必须和调用者servlet或filter传入的包装对象是相同的。
在部署描述符中,可以使用<init-params>元素来关联filter的初始化参数。在运行时,这些参数的names和values对于filter来说可以通过filter的FilterConfig对象的getInitParameter和getInitParameterNames方法获得。另外,FilterConfig提供了对web application的ServletContext的访问功能,以便可以装载资源,提供日志功能,以及对ServletContext的attribute列表的存储。
在部署描述符中,filter使用<filter>元素定义。在这个元素中,程序员应定义如以下描述:
还有一些可选的功能,程序员可以定义图标,文本的描述,and a display name for tool manipulation 。Container会按照部署描述符中所定义的,一个filter声明对应一个java class的实例。因此,如果开发者用同样的filter类定义了两个filter的声明,那么同一个filter class就会有两个实例被实例化。
这里有一个filter声明的例子:SRV.6.2.4-1.png
一旦在部署描述符中描述声明了一个filter,就需要使用<filter-mapping>元素来定义filter需要作用于的内容,它们可以是web application中的servlets和静态resources。比如,以下的代码片段把Image Filter filter到了ImageServlet这个servlet:SRV.6.2.4-2.png
Filters可以关联至一组servlets和静态内容,通过<url-pattern>风格的filter mapping:SRV.6.2.4-3.png
此处,Logging Filter被作用于web application中所有的servlets和静态内容,因为每个匹配“/*”的request URL。
当使用<url-pattern>风格的元素去执行<filter-mapping>元素的时候,container必须判断request URL是否和<url-pattern>匹配,路径匹配规则参见SRV.11,“Mapping Requests to Servlets”。
container在构建用来匹配某一特定request URL的filter chain时,其处理过程如下:
这样的要求意味着container在接收到request时,会按照下面的内容处理reqeust:
高性能的web container会缓存filter chain以便它们不必基于每一个request去计算filter chain。
在Java servlet 规范v2.4中的关于filter的新增内容是,通过配置使filter可以在request dispatcher的forward()及include()之下被调用的能力。
通过在部署描述符中使用新标签<dispatcher>元素,开发者可以在filter-mapping中指出,在以下情况出现的时候,他是否需要filter作用于request:
举例说明:
SRV.6.2.5-1.png
客户端以products/...开头的请求,都会触发Logging Filter的调用。但并不会触发request dispatcher的调用。再看下面的代码:
SRV.6.2.5-2.png
客户端对ProductServlet的请求,都不会触发Logging Filter的调用,也不会在forward()到ProductServlet的时候被调用调用。只有request dispatcher 调用到ProductServlet并且执行include()方法时才会触发。
最终地,
SRV.6.2.5-3.png
would result in the Logging Filter being invoked by client requests starting /products/... and underneath a request dispatcher forward() call where the request dispatcher has path commencing /products/... (貌似有问题)。