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的开发者来说,下面是filter一些有用的特性:
可以在Request调用资源前访问资源。
可以在对资源的Request执行前被调用。
可以针对包装过的自定义版本request的request header及data进行修改。
可以针对包装过的自定义版本response的response header及data进行修改。
在资源被调用后拦截资源(The interception of an invocation of a resource after its call)。
以特定的顺序,将零个、一个、或多个filter作用于一个servlet,一组servlet,或者静态内容。
Authentication filters
Logging and auditing filters
Image conversion filters
Data compression filters
Encryption filters
Tokenizing filters
Filters that trigger resource access events
XSL/T filters that transform XML content
MIME-type chain filters
Caching filters
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的headers
为了修改request headers或者data,doFilter方法可以把传入的request对象用ServletRequest或HttpServletRequest接口的自定义实现包装request对象。
为了修改request headers或者data,doFilter方法可以用ServletResponse或HttpServletResponse接口的自定义实现包装request对象。
Filter可以调用filter chain中的下一个实体。下一个实体可以是另一个filter,或者如果filter做的调用是部署描述符中配置的最后一个filter,那下一个实体就是目标web resource。下一个实体的调用通过调用filter chain对象上的doFilter方法完成,并且把request和response传递进去。
Filter chain实现的doFilter方法,通过container提供,必须找出在filter chain中的下一个实体并且调用它的doFilter方法,传进恰当的request和response对象。
非此即彼的,filter chain可以通过不对下一个实体进行调用的方式阻塞request,把填写response对象的责任留给自己。
在对filter chain中下一个filter的调用之后,filter可以检验response headers。
二选一的,filter有可能在执行过程中抛出一个异常来指示一个错误。如果filter在执行doFilter的过程中抛出了UnavailableException,那么container一定不会试图继续执行下面的filter chain。如果exception没有标记为永久的话,它会在接下来的某个时间重新尝试整个chain。
当filter chain中最后一个filter被调用时,下一个要被存取的实体就是目标servlet或者在chain中最后面的资源。
在filter实例从container的服务中被移除之前,container必须首先调用filter上的destroy方法来使filter释放掉所有的资源并且执行其他清理操作
过滤的核心理念是为了包装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>元素定义。在这个元素中,程序员应定义如以下描述:
filter-name:用来把filter映射到一个servlet或URL
filter-class:container使用它来定义filter的类型
init-params: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时,其处理过程如下:
首先,<url-pattern>匹配filter mapping的顺序和出现在部署描述符中的顺序一致。
其次,<servlet-name>匹配filter mapping的顺序和出现在部署描述符中的顺序一致。
这样的要求意味着container在接收到request时,会按照下面的内容处理reqeust:
根据SRV.11.2 Specifications of Mappings提到的规则来识别目标web 资源。
如果有filter与servlet name相匹配,并且web资源有<servlet-name>元素,container会依照部署描述符中声明的状态建立filter chain。The last filter in this chain corresponds to the last <servlet-name> matching filter and is the filter that invokes the target Web resource.
如果有filter使用<url-pattern>匹配并且根据SRV.11.2 Specification of Mapping的规则url-pattern匹配request URL,container会依照部署描述符中声明的<url-pattern>的顺序建立chain。The last filter in this chain is the filter that invokes the first filter in the <servlet-name> matching chain, or invokes the target Web resource if there are none.
高性能的web container会缓存filter chain以便它们不必基于每一个request去计算filter chain。
在Java servlet 规范v2.4中的关于filter的新增内容是,通过配置使filter可以在request dispatcher的forward()及include()之下被调用的能力。
通过在部署描述符中使用新标签<dispatcher>元素,开发者可以在filter-mapping中指出,在以下情况出现的时候,他是否需要filter作用于request:
request直接来自client。这种情况可以通过给<dispatcher>元素设置value为REQUEST来指明,或者不配置任何<dispatcher>元素。
在request dispatcher之下被执行的request表示web组件通过调用forward()来匹配<url-pattern>或者<servlet-name>。这种情况通过给<dispatch>元素设置value为FORWARD来声明。
在request dispatcher之下被执行的request表示web组件通过调用forward()来匹配<url-pattern>或者<servlet-name>。这种情况通过给<dispatch>元素设置value为INCLUDE来声明。
Request被SRV.9.9 Error Handling描述的错误页面机制处理来匹配<url-pattern>。这种情况通过给<dispatch>元素设置value为ERROR来声明。
或者以上情况的任意组合。
举例说明:
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/... 。