文章是对 JSR-000340 JavaTM Servlet 3.1 Final Release的Java™ Servlet规范的翻译,尚未校准
过滤器是一种Java组件,允许对进入资源的请求和来自资源的响应中的负载和头信息进行即时转换。 Java Servlet API的类和方法为过滤活动和静态内容提供了一个轻量级框架。它描述了如何在Web应用程序中配置过滤器,以及实现过滤器的约定和语义。Servlet过滤器的API文档是在线提供的。过滤器的配置语法由第14章 "Deployment Descriptor "中的部署描述符模式给出。读者在阅读本章时应将这些资料作为参考。
过滤器是一段可重复使用的代码,它可以改变HTTP请求、响应和头信息的内容。过滤器一般不象servlets那样创建响应或对请求作出响应,而是修改或调整对资源的请求,并修改或调整来自资源的响应。过滤器可以作用于动态或静态内容。在本章中,动态和静态内容被称为Web资源。在需要使用过滤器的开发者可用的功能类型中,有以下几种:
本节将描述这个过滤模型的主要概念。应用程序开发人员通过实现javax.servlet.Filter接口并提供一个不需要参数的公共构造函数来创建一个过滤器。该类与构成Web应用程序的静态内容和Servlet一起被打包在Web Archive 中。使用部署描述符中的元素来声明过滤器。过滤器或过滤器的集合可以通过在部署描述符中定义 元素来配置调用。这是通过将过滤器通过servlet的逻辑名称映射到一个特定的servlet,或者通过将过滤器映射到一个URL模式,映射到一组servlet和静态内容资源。
在部署 Web 应用程序之后,在请求导致容器访问 Web 资源之前,容器必须找到必须应用于 Web 资源的过滤器列表,如下所述。容器必须确保它已经为列表中的每个过滤器实例化了一个适当类别的过滤器,并调用其 init(FilterConfig config) 方法。过滤器可能会抛出一个异常,表示它不能正常工作。如果该异常是 UnavailableException 类型,容器可以检查该异常的isPermanent属性,并可以选择在以后的某个时间重试该过滤器。
部署描述符中的每个 声明只有一个实例被实例化到容器的JVM中。容器提供过滤器部署描述符中声明的过滤器config,对Web应用程序的ServletContext的引用,以及一组初始化参数。
当容器接收到一个传入的请求时,它将采取列表中的第一个过滤器实例,并调用其doFilter方法,传入ServletRequest和ServletResponse,以及它将使用的FilterChain对象的引用。
过滤器的 doFilter方法通常会按照这个模式或以下模式的某个子集来实现。
该方法检查了请求的头信息。
该方法可以用ServletRequest或HttpServletRequest的自定义实现来包装请求对象,以便修改请求头或数据。
该方法可以用ServletResponse或HttpServletResponse的自定义实现来包装传递给其doFilter方法的响应对象,以修改响应头或数据。
过滤器可以调用过滤器链中的下一个entity 。下一个entity 可能是另一个过滤器,或者如果调用的过滤器是这个链的部署描述符中配置的最后一个过滤器,下一个entity 就是目标Web资源。下一个entity 的调用是通过调用 FilterChain 对象上的 doFilter 方法来实现的,并传入被调用的请求和响应,或者传入它可能已经创建的封装版本。
容器提供的过滤器链的doFilter`方法的实现必须定位过滤器链中的下一个entity ,并调用其doFilter方法,传入适当的请求和响应对象。
或者,过滤器链可以通过不调用下一个entity来阻止请求,让过滤器负责填写响应对象。
service方法需要与适用于servlet的所有过滤器在同一线程中运行。
在调用链中的下一个过滤器后,该过滤器可以检查响应头。
或者,过滤器可能抛出了一个异常,表明处理过程中出现了错误。如果过滤器在其doFilter处理过程中抛出一个 UnavailableException,容器就不能尝试继续沿着过滤器链进行处理。如果该异常没有被标记为永久性的,它可以选择在稍后的时间重试整个链。
当链中的最后一个过滤器被调用后,下一个被访问的entity是链末端的目标Servlet或资源。
在容器可以从服务中移除过滤器实例之前,容器必须首先调用过滤器的 destroy 方法,以使过滤器能够释放任何资源并执行其它清理操作。
过滤概念的核心是包装请求或响应的概念,以便它可以覆盖行为来执行过滤任务。在这个模型中,开发者不仅能够覆盖请求和响应对象上的现有方法,而且能够提供适合特定过滤任务的新API给过滤器或链上的目标网络资源。例如,开发者可能希望用更高层次的输出对象来扩展响应对象,如允许DOM对象被写回给客户端的API。
为了支持这种风格的过滤器,容器必须支持以下要求。当过滤器调用容器的过滤链实现上的doFilter方法时,容器必须确保它传递给过滤链中的下一个实体的请求和响应对象,或者如果过滤器是过滤链中的最后一个实体,则传递给目标网络资源的请求和响应对象是调用过滤器传递到doFilter方法中的同一个对象。
当调用者包装请求或响应对象时,对包装对象身份的要求同样适用于从Servlet或过滤器对RequestDispatcher.forward或RequestDispatcher.include的调用。在这种情况下,被调用的servlet所看到的请求和响应对象必须是调用的servlet或过滤器所传入的相同的包装器对象。
一组初始化参数可以使用部署描述符中的元素与过滤器相关联。这些参数的名称和值在运行时通过过滤器的FilterConfig对象上的getInitParameter和getInitParameterNames方法提供给过滤器。此外,FilterConfig允许访问Web应用程序的 ServletContext,以加载资源、记录功能,并在ServletContext的属性列表中存储状态。过滤器和过滤器链末端的目标Servlet或资源必须在同一个调用线程中执行。
过滤器是通过规范第8-69页的8.1.2节"@WebFilter "中定义的@WebFilter 注解或在部署描述符中使用元素定义的。在这个元素中,程序员声明了以下内容:
filter-name: used to map the filter to a servlet or URL用来将过滤器映射到一个Servlet或URL上。filter-class: used by the container to identify the filter type容器用于识别过滤器的类型init-params: initialization parameters for a filter滤波器的初始化参数作为可选项,程序员可以指定图标、文本描述和用于工具操作的显示名称。容器必须为部署描述符中的每个过滤器声明准确地实例化一个定义过滤器的 Java 类。因此,如果开发者为同一个过滤器类做了两个过滤器声明,那么同一个过滤器类的两个实例将被容器实例化。
Here is an example of a filter declaration:
<filter>
<filter-name>Image Filterfilter-name>
<filter-class>com.acme.ImageServletfilter-class>
filter>
一旦在部署描述符中声明了过滤器,汇编器就会使用元素来定义Web应用程序中要应用过滤器的servlet和静态资源。过滤器可以使用元素与servlet关联。
例如,下面的代码示例将图像过滤器映射到ImageServletservlet:
<filter-mapping>
<filter-name>Image Filterfilter-name>
<servlet-name>ImageServletservlet-name>
filter-mapping>
过滤器可以使用 样式的过滤器映射与一组servlet和静态内容关联:
<filter-mapping>
<filter-name>Logging Filterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
在这里,日志过滤器被应用于Web应用程序中的所有Servlet和静态内容页面,因为每个请求URI都与/*URL模式匹配。
当使用 样式处理 元素时,容器必须使用第12章 "Mapping Requests to Servlets"中定义的路径映射规则来确定是否与请求URI匹配。
容器在建立应用于特定请求URI的过滤器链时使用的顺序如下:
matching filter mappings in the same order that these elements appear in the deployment descriptor. 首先,匹配过滤器映射,其顺序与这些元素在部署描述符中出现的顺序相同。 matching filter mappings in the same order that these elements appear in the deployment descriptor.接下来是匹配的过滤器映射,其顺序与这些元素在部署描述符中出现的顺序相同。如果一个过滤器映射同时包含和,容器必须将过滤器映射扩展为多个过滤器映射(每个和),保留和元素的顺序。例如,下面的过滤器映射:
<filter-mapping>
<filter-name>Multipe Mappings Filterfilter-name>
<url-pattern>/foo/*url-pattern>
<servlet-name>Servlet1servlet-name>
<servlet-name>Servlet2servlet-name>
<url-pattern>/bar/*url-pattern>
filter-mapping>
is equivalent to:
<filter-mapping>
<filter-name>Multipe Mappings Filterfilter-name>
<url-pattern>/foo/*url-pattern>
filter-mapping>
<filter-mapping>
<filter-name>Multipe Mappings Filterfilter-name>
<servlet-name>Servlet1servlet-name>
filter-mapping>
<filter-mapping>
<filter-name>Multipe Mappings Filterfilter-name>
<servlet-name>Servlet2servlet-name>
filter-mapping>
<filter-mapping>
<filter-name>Multipe Mappings Filterfilter-name>
<url-pattern>/bar/*url-pattern>
filter-mapping>
关于过滤器链的顺序的要求意味着容器在接收到一个传入的请求时,将按以下方式处理该请求:
,那么容器将按照部署描述符中声明的顺序建立匹配的过滤器链。这个链中的最后一个过滤器与最后一个匹配的过滤器相对应,是调用目标Web资源的过滤器。 匹配的过滤器,并且 根据第 12.2 节 "Specification of Mappings "的规则与请求的 URI 匹配,则容器将按照部署描述符中声明的相同顺序构建 匹配的过滤器链。这个链中的最后一个过滤器是这个请求URI的部署描述符中最后一个匹配过滤器。该链中的最后一个过滤器是调用匹配链中的第一个过滤器的过滤器,如果没有,则调用目标Web资源。预计高性能的Web容器将缓存过滤器链,以便它们不需要在每个请求的基础上计算它们。
自Java Servlet规范的2.4版本以来,新的功能是配置过滤器,以便在请求调度器下调用forward()和include()。
通过在部署描述符中使用新的元素,开发者可以为过滤器映射指出他是否希望过滤器在什么时候被应用到请求:
The request comes directly from the client.请求直接来自客户端。
This is indicated by a element with value REQUEST, or by the absence of any elements. 这由一个值为REQUEST的元素表示,或者由没有任何元素表示。
The request is being processed under a request dispatcher representing the Webcomponent matching the or using a forward() call.请求正在由代表与或servlet-name>匹配的Webcomponent的请求调度器使用forward()调用进行处理。
This is indicated by a element with value FORWARD.这由一个值为FORWARD的元素来表示。
The request is being processed under a request dispatcher representing the Web component matching the or using an include() call.该请求正在由代表与或相匹配的Web组件的请求调度器使用include()调用进行处理。
This is indicated by a element with value INCLUDE.这由一个值为INCLUDE的元素表示。
The request is being processed with the error page mechanism specified in ”Error Handling” on page 108 to an error resource matching the .该请求正在用第108页 "错误处理 "中指定的错误页面机制来处理与匹配的错误资源。
This is indicated by a element with the value ERROR. 这将由一个值为ERROR的元素来表示。
The request is being processed with the async context dispatch mechanism specified in ”Asynchronous processing” on page 10 to a web component using a dispatch call.请求正在用第10页 "Asynchronous processing "中指定的异步上下文调度机制进行处理,到一个使用调度调用的Web组件。
This is indicated by a element with the value ASYNC. 这由一个值为ASYNC的元素来表示。
或上述1、2、3、4或5的任何组合。
For example:
<filter-mapping>
<filter-name>Logging Filterfilter-name>
<url-pattern>/products/*url-pattern>
filter-mapping>
将导致日志过滤器被以/products/...开头的客户端请求调用,但在请求调度器有以/products/...开头的路径时,不会在请求调度器调用下调用。LoggingFilter将在请求的初始调度和恢复的请求中被调用。
下面的代码:
<filter-mapping>
<filter-name>Logging Filterfilter-name>
<servlet-name>ProductServletservlet-name>
<dispatcher>INCLUDEdispatcher>
filter-mapping>
这将导致日志过滤器不被客户端对 ProductServlet的请求所调用,也不在对 ProductServlet的请求调度器forward()调用之下,而是在请求调度器include()调用之下被调用,其中请求调度器的名称以ProductServlet开头。
下面的代码:
<filter-mapping>
<filter-name>Logging Filterfilter-name>
<url-pattern>/products/*url-pattern>
<dispatcher>FORWARDdispatcher>
<dispatcher>REQUESTdispatcher>
filter-mapping>
这将导致日志过滤器被始于/products/...的客户端请求调用,同时在路径始于/products/...的请求调度器forward()的情况下被调用。
最后,以下代码使用了特殊的servlet名称’*'。
<filter-mapping>
<filter-name>All Dispatch Filterfilter-name>
<servlet-name>*servlet-name>
<dispatcher>FORWARDdispatcher>
filter-mapping>
这段代码将导致在请求调度器forward()调用时调用所有通过名称或路径获得的请求调度器的全部调度过滤器。