Servlet 2.4 规范之第七篇:过滤器

过滤器是一套java组件,用于在请求—>资源—>应答的这一过程中即时转换处理负载和头信息。

本章讲述了Servlet 2.4 API中一些类和方法,这些类和方法提供了一套轻量级框架用于过滤动态和静态内容。它讲述了web应用下如何配置过滤器、指定过滤器的实现类。

servlet过滤器的API会在SRV.14的“javax.servlet”章节提供,而其配置语法会在SRV.13章“部署描述符”的语法中讲解。读者阅读本章的同时也应该参考下对应的章节内容。

SRV.6.1    什么是过滤器

过滤器是一段用于转换HTTP请求、响应和头信息的可重用代码段。它不能创建响应对象,一般只用于修改或适配请求和响应过程中的资源处理。

过滤器可以处理动态或静态内容,在本章里,动态或静态内容主要涉及web资源。

开发人员主要在以下场景下使用过滤器:

1. 请求之前访问资源

2. 访问资源之前对请求做处理

3. 通过修改请求的头信息和信息体来封装一个自定义版本的请求对象

4. 通过修改响应的头信息和信息体来提供一个自定义版本的响应对象

5. 在过滤器调用时插入对指定资源的访问请求

6. 通过零到多个指定顺序的过滤器来操作servlet和静态内容

SRV.6.1.1    常见过滤器示例

• 授权过滤器

• 日志和审计过滤器

• 图像转换过滤器

• 数据压缩过滤器

• 加密过滤器

• 令牌过滤器

• 触发资源访问事件的过滤器

• XML内容转换过滤器

• MIME-type 内容链过滤器

• 缓存过滤器

SRV.6.2    主要概念

过滤器模式的主要概念在本章描述。

开发人员通过实现javax.servlet.Filter接口然后提供一个公共无参构造方法来创建过滤器。它会被打包进Web压缩包,正如构成web应用的静态内容和servlet一样。过滤器通过部署描述符中的<filter>元素进行声明。一个或一组过滤器然后通过定义<filter-mapping>元素来配置如何被调用。这个配置通过把过滤器映射到特定的servlet上来完成,或者把一组serlvet和静态内容映射到一个URL模式的过滤器上。

SRV.6.2.1    过滤器的生命周期

在部署应用系统之后容器访问web资源之前,容器必须定位用于访问web资源的所有过滤器。容器必须确保每一个过滤器在调用init(FilterConfig config)方法之后被正确的初始化。过滤器可以抛出异常表示它无法正常工作,如果异常是UnavailableException类型,容器必须检查它的isPermanect属性并可能在一段时间后重试这个过滤器。

一个过滤器实例只会被同一个容器的同一个JVM初始化一次容器负责提供过滤器的config对象,正如在部署描述中声明的那样,另外提供对ServletContext的引用和一系列初始化参数。

当容器收到传入的请求时,它通过doFilter方法调用过滤器列表中的第一个过滤器实例,并传入ServletRequest和ServletResponse,以及容器使用的FilterChain对象的引用。

过滤器的doFilter方法通常实现于以下模式里:

步骤1:检查请求头信息

步骤2:用自定义实现的ServletRequest或HttpServletRequest来封装请求对象,用于修改请求头或信息体。

步骤3:用自定义实现的ServletResponse或HttpServletResponse来封装响应对象,用于修改响应头或信息体。

步骤4:过滤器可能会调用过滤器链上的下一个实体对象,它可以是另一个过滤器,若本身已是最后一个过滤器节点,那下一个实体就是目标web资源。这个调用过程由FilterChain对象的doFilter方法完成,同时也会传递被调用的请求和响应对象。

容器提供的doFilter方法的过滤器链实现,必须定位下一个实体并调用对应的doFilter方法,并随之传递合适的请求和响应对象。

换句话说,过滤器链可以通过不调用下一个实体对象来阻塞请求的处理流程,留下的过滤器负责填充输出响应对象。

步骤5:下一个责任链实体调用之后,过滤器可以检查响应头信息。

步骤6:必要情况下过滤器可以抛出异常以示发生错误。如果过滤器在doFilter执行期间抛出UnavailableException,容器就不必再尝试继续处理剩下的流程。如果异常未标记为永久性的话,那可以等一段时间后再重试整个过滤器链。

步骤7:链条上的最后一个过滤器被调用之后,下一个被访问对象会是目标servlet或者链条末端的资源。

步骤8:容器删除一个过滤器的实例之前,必须先调用过滤器的destroy方法,用以释放所有的占用资源并执行清理操作。

SRV.6.2.2    封装请求和响应

过滤器的本质就是对请求或响应的封装,以便于能重写对应的行为方式去执行些过滤任务。在此模式下,开发人员不但负责重写请求和响应对象已存在的方法,而且还要提供新的API集给链条上接下来的其他过滤器和web资源。例如,开发人员可能想增强响应对象的输出能力,像允许DOM对象输出到客户端的API之类的情形。

为了支持这些过滤器,容器必须满足以下需求。当过滤器调用doFilter方法时,容器必须确保请求和响应对象在整个责任链条上始终一致。

这些需求也同样适用于RequestDispatcher.forward或RequestDispathcer.include,当他们想封装请求和响应对象时。

SRV.6.2.3    过滤器环境

一组初始化参数可以通过部署描述符中的<init-params>元素与过滤器关联起来。过滤器在运行时可以通过getInitParameter和getInitParameterNames方法来访问这些名值对。另外,FilterConfig提供了一条访问web应用系统的ServletConfig的途径。

SRV.6.2.4    Web应用中的过滤器配置

一个过滤器对应于部署描述符下的一个<filter>元素,在此元素中,开发人员可以声明以下内容:

• filter-name:  用于映射过滤器到一个servlet或URL下
• filter-class: 由容器用于标识唯一的过滤器类
• init-params: 过滤器的初始化参数

必要情况下,开发人员还能指定图标、文本描述和工具封装时使用的显示名。容器必须精确地对每一个过滤器的声明对应初始化一个Java类实例。如果两个过滤器声明指定了同一个Java类,那么容器就会对应的为这两个过滤器分别初始化一份Java类。

示例:

<filter>
    <filter-name>Image Filter</filter-name>
    <filter-class>com.acme.ImageServlet</filter-class>
</filter>

部署描述符一旦声明一个过滤器,对应的就会使用<filter-mapping>元素定义配套的servlet或静态资源。过滤器通过<servlet-name>元素和某个servlet关联起来。例如,以下代码把Image Filter映射到了ImageServlet这个servlet上:

<filter-mapping>
    <filter-name>Image Filter</filter-name>
    <servlet-name>ImageServlet</servlet-name>
</filter-mapping>

过滤器还可以通过<url-pattern>元素和一组serlvet或静态内容建立对应的关联关系:

<filter-mapping>
    <filter-name>Logging Filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

这个例子里Loggin Filter会被用于所有的servlet和静态页面,因为每一个请求URL都和’/* ’相匹配。

当使用<url-pattern>方式处理<filter-mapping>元素时,容器必须检测<url-pattern>是否匹配请求的URL。

容器按照以下先后顺序编译过滤器链,用以处理对应的请求URI:

1. 首先,按照部署描述符中出现的先后顺序,<url-pattern>依次匹配过滤器映射。

2. 下一步,按照部署描述符中出现的先后顺序,<servlet-name>依次匹配过滤器映射。

这个需求意味着当容器收到请求时按照以下说明依次处理:

按照“映射规范”标识出目标Web资源。

若通过servlet名和web资源匹配到的过滤器有<servlet-name>,容器会按照部署描述符中出现的先后顺序依次编译创建对应的过滤器责任链。最后一个过滤器对应于最后一个<servlet-name>,并且它会用于调用最终的web资源。

若使用<url-pattern>模式的过滤器匹配到了请求的URI,容器按照过滤器在部署描述符中出现的先后顺序依次编译创建对应的过滤器责任链。过滤器链条最后一个过滤器是部署描述符中这个请求最后一个<url-pattern>匹配的过滤器。链条中的最后一个过滤器调用<servlet-name>匹配链条中的第一个过滤器,如果没有,就调用目标web资源。

专业一点来说高性能web容器都应该缓存过滤器责任链,这样一来就不用每次请求时都进行对应的计算了。

SRV.6.2.5    过滤器和请求分发器

个人理解上尚有不完善,待译。。。

文档信息

你可能感兴趣的:(servlet)