servlet3.1规范: 第6章 过滤器(Filter)

15 篇文章 0 订阅

转载: Servlet规范

第6章 过滤器

过滤器是允许动态改变负载以及到资源的请求和来自资源的响应中的头信息的Java组件。

本章描述了Java Servlet v3.0 API类和方法,它们提供了一种轻量级的框架用于过滤动态和静态内容。还描述了如何在Web应用中配置Filter,以及它们的实现的约定和语义。

网上提供了Servlet过滤器的API文档。过滤器的配置语法在第14章的”部署描述符”中的部署描述符模式部分给出。当阅读本章时读者可以使用这些资源作为参考。

6.1 什么是过滤器

过滤器是一种代码重用的技术,它可以改变HTTP请求的内容,响应,及header信息。过滤器通常不会像Servlet那样创建响应或响应请求,而是修改或调整到资源的请求,以及修改或调整来自资源的响应。

过滤器可以作用于动态或静态内容。这章说的动态和静态内容指的是Web资源。

供开发人员使用的过滤器功能有如下几种类型:

■ 在执行请求之前访问资源。
■ 在执行请求之前处理资源的请求。
■ 用请求对象的自定义版本包装请求对请求的header和数据进行修改。
■ 用响应对象的自定义版本包装响应对响应的header和数据进行修改。
■ 拦截资源调用之后的调用。
■ 作用在Servlet,一组Servlet,或静态内容上的零个,一个或多个过滤器按指定的顺序执行。

6.1.1 过滤器组件示例

■ 验证过滤器
■ 日志记录和审计过滤器
■ 图像转换过滤器
■ 数据压缩过滤器
■ 加密过滤器
■ 词法(Tokenizing)过滤器
■ 触发资源访问事件过滤器
■ 转换XML内容的XSL/T过滤器
■ MIME-类型链过滤器
■ 缓存过滤器

6.2 主要概念

本章描述了过滤器模型的主要概念。

应用开发人员通过实现javax.servlet.Filter接口并提供一个公共的空参构造器来创建过滤器。该类及构建Web应用的静态资源和Servlet打包在Web应用归档文件中。Filter在部署描述符中通过<filter>元素声明。一个过滤器或一组过滤器可以通过在部署描述符中定义<filter-mapping>来为调用配置。可以使用servlet的逻辑名称把过滤器映射到一个特定的servlet,或者通过映射一个过滤器到一个URL模式来把过滤器映射到一组Servlet和静态内容资源。

6.2.1 过滤器生命周期

在Web应用部署之后,在请求导致容器访问Web资源之前,容器必须找到过滤器列表并按照如上下所述的应用到Web资源。容器必须确保它为过滤器列表中的每一个都实例化了一个适当类的过滤器,并调用其init(FilterConfig config)方法。过滤器可能会抛出一个异常,以表明它不能正常运转。如果异常的类型是UnavailableException,容器可以检查异常的isPermanent属性并可以选择稍候重试过滤器。

在部署描述符中声明的每个<filter>在每个JVM的容器中仅实例化一个实例。容器提供了在过滤器的部署描述符中声明过滤器配置(译者注:FilterConfig),对Web应用的ServletContext的引用,和一组初始化参数。

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

过滤器的doFilter方法​​通常会被实现为如下或如下形式的子集:

1). 该方法检查请求的头。

2). 该方法可以用自定义的ServletRequest或HttpServletRequest实现包装请求对象来修改请求的头或数据。

3). 该方法可以用自定义的ServletResponse或HttpServletResponse实现包装传入doFilter方法的响应对象用于修改响应的头或数据。

4). 该过滤器可以调用过滤器链中的下一个实体。下一个实体可能是另一个过滤器,或者如果当前调用的过滤器是该过滤器链配置在部署描述符中的最后一个过滤器,下一个实体是目标Web资源。调用FilterChain对象的doFilter方法将影响下一个实体的调用,且传入它被调用时的请求和响应,或传入它可能已经创建的包装版本。

由容器提供的过滤器链的doFilter方法的实现,必须找出过滤器链中的下一个实体并调用它的doFilter方法,传入适当的请求和响应对象。另外,过滤器链可以通过不调用下一个实体来阻止请求,离开让过滤器负责填充响应对象。

service方法必须和应用到servlet的所有过滤器运行在同一个线程中。

5). 过滤器链中的下一个过滤器调用之后,过滤器可能检查响应的头。

6). 另外,过滤器可能抛出一个异常以表示处理过程中出错了。如果过滤器在doFilter处理过程中抛出UnavailableException,容器必须停止处理剩下的过滤器链。如果异常没有标识为永久的,它或许选择稍候重试整个链。

7). 当链中的最后的过滤器被调用,下一个访问的实体是链最后的目标servlet或资源。

8). 在容器能把服务中的过滤器实例移除之前,容器必须先调用过滤器的destroy方法以便过滤器释放资源并执行其他的清理工作。

6.2.2 包装请求和响应

过滤器的核心概念是按顺序包装请求或响应,以便它可以覆盖行为执行过滤任务。在这个模型中,开发人员不仅可以覆盖请求和响应对象上已有的方法,也能提供新的API以适用于对过滤器链中剩下的过滤器或目标web资源做特殊的过滤任务。例如,开发人员可能希望用更高级别的输出对象如output stream 或 writer来扩展响应对象,如允许把DOM对象写回客户端的API。

为了支持这种风格的过滤器,容器必须支持如下要求。当过滤器调用容器过滤器链实现的doFilter方法时,容器必须确保传递到过滤器链中下一个实体或目标web资源(如果过滤器是链中的最后一个)的请求和响应对象与调用当前过滤器传入doFilter方法的请求和响应是同一个对象。

当调用者包装请求或响应对象时,对包装对象的要求同样适用于从servlet或过滤器到RequestDispatcher.forward 或 RequestDispatcher.include的调用。在这种情况下,调用servlet看到的请求和响应对象与调用servlet或过滤器传入的包装对象必须是一样的。

6.2.3 过滤器环境

可以使用部署描述符中的<init-params>元素把一组初始化参数关联到过滤器。这些参数的名字和值在过滤器运行期间可以使用过滤器的FilterConfig对象的getInitParameter和getInitParameterNames方法得到。另外,FilterConfig提供访问Web应用的ServletContext用于加载资源,记录日志,存储ServletContext的属性列表中的状态。链中最后的过滤器和目标servlet或资源必须执行在同一个调用线程。

6.2.4 在Web应用中配置过滤器

过滤器可以通过规范的8.1.2节“@WebFilter”的@WebFilter注解定义或者在部署描述符中使用<filter>元素定义。在这个元素中,程序员可以声明如下内容:

■ filter-name: 用于映射过滤器到servlet或URL
■ filter-class: 由容器用于表示过滤器类型
■ init-params: 过滤器的初始化参数

程序员可以选择性的指定icon图标,文字说明,和工具操作显示的名字。容器必须为部署描述符中定义的每个过滤器声明实例化一个Java类实例。因此,如果开发人员对同一个过滤器类声明了两次,则容器将实例化两个相同的过滤器类的实例。

下面是一个过滤器声明的例子:

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

一旦在部署描述符中声明了过滤器,装配器使用<filter-mapping>元素来定义Web应用中要应用过滤器的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>风格的过滤器映射关联一组servlet和静态内容:

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

在这里, Logging Filter过滤器应用到Web应用中的所有servlet和静态资源,因为每一个请求的URI都匹配‘/*’URL模式。

当使用<url-pattern>风格配置<filter-mapping>元素,容器必须使用定义在第12章“映射请求到Servlet”中的路径映射规则(path mapping rule)决定<url-pattern>是否匹配请求URI。

容器使用的用于构建应用到一个特定请求URI的过滤器链的顺序如下所示:

  1. 首先, <url-pattern>按照在部署描述符中的出现顺序匹配过滤器映射。
  2. 接下来,<servlet-name>按照在部署描述符中的出现顺序匹配过滤器映射。

如果过滤器映射同时包含了<servlet-name><url-pattern>,容器必须展开过滤器映射为多个过滤器映射(每一个<servlet-name><url-pattern>),保持<servlet-name><url-pattern>元素顺序。例如,以下过滤器映射:

<filter-mapping>  
   <filter-name>Multipe Mappings Filter</filter-name>  
   <url-pattern>/foo/*</url-pattern>  
   <servlet-name>Servlet1</servlet-name>  
   <servlet-name>Servlet2</servlet-name>  
   <url-pattern>/bar/*</url-pattern>  
</filter-mapping>  

等价于:

<filter-mapping>  
    <filter-name>Multipe Mappings Filter</filter-name>  
    <url-pattern>/foo/*</url-pattern>  
</filter-mapping>  
<filter-mapping>  
    <filter-name>Multipe Mappings Filter</filter-name>  
    <servlet-name>Servlet1</servlet-name>  
</filter-mapping>  
<filter-mapping>  
    <filter-name>Multipe Mappings Filter</filter-name>  
    <servlet-name>Servlet2</servlet-name>  
</filter-mapping>  
<filter-mapping>  
    <filter-name>Multipe Mappings Filter</filter-name>  
    <url-pattern>/bar/*</url-pattern>  
</filter-mapping>

关于过滤器链顺序的要求意味着当容器接收到传入的请求,按照如下方式来处理请求:

■ 按照”映射规则”识别目标Web资源。
■ 如果有过滤器使用servlet name匹配到具有<servlet-name>的Web资源,容器以声明在部署描述符中的顺序构建过滤器链。该链中的最后一个过滤器,即最后一个<servlet-name> 匹配的过滤器将调用目标Web资源。
■ 如果有过滤器使用<url-pattern>匹配且该<url-pattern>按照第12.2节”映射规则”中的规则匹配请求的URI,容器以声明在部署描述符中的顺序构建<url-pattern>匹配到的过滤器链。该链中的最后一个过滤器是在部署描述符中对当前请求URI最后一个<url-pattern>匹配的过滤器。链中的最后一个过滤器将调用<servlet-name>匹配的链中的第一个过滤器,或如果没有过滤器,则调用目标Web资源。

想要提高性能,Web容器将缓存过滤器链从而不需要根据每个请求重新计算它们。

6.2.5 过滤器和RequestDispatcher

Java Servlet规范2.4版本以来可根据请求分派器的forward()和include()调用来配置被调用的过滤器。

通过在部署描述符中使用新的<dispatcher>元素,开发人员可通过filter-mapping来表明是否想要把过滤器应用到请求,当:

  1. 请求直接来自客户端。
    可以由一个带有REQUEST值的<dispatcher>元素来表示,或者不使用任何<dispatcher>元素(默认就是REQUEST)。

  2. 处理使用请求分派器forward()调用传递过来的请求表示Web组件匹配<url-pattern><servlet-name>模式。
    可以由一个带有FORWARD值的<dispatcher>元素表示。

  3. 处理使用请求分派器include()调用传递过来的请求表示Web组件匹配<url-pattern><servlet-name>模式。
    可以由一个带有INCLUDE值的<dispatcher>元素表示。

  4. 使用”错误处理”指定的错误页面机制处理匹配<url-pattern>的错误资源的请求。
    可以由一个带有ERROR值的<dispatcher>元素表示。

  5. 使用”异步处理”中的异步上下文分派机制来处理Web组件调用dispatch传递过来的请求。
    可以由一个带有ASYNC值的<dispatcher>元素表示。

  6. 或上面1,2,3,4或5的任何组合。

例如:

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

以/products/…开始的客户端请求将导致Logging Filter被调用,但不是一个以路径/products/…开始的请求分派器调用。LoggingFilter 将在请求初始分派和恢复请求时被调用。

如下代码:

<filter-mapping>  
   <filter-name>Logging Filter</filter-name>  
   <servlet-name>ProductServlet</servlet-name>  
   <dispatcher>INCLUDE</dispatcher>  
</filter-mapping>  

到ProductServlet的客户端请求将不会导致LoggingFilter被调用,通过请求分派器forward()调用传递过来的到ProductServlet的请求也不会导致Logging Filter被调用,只有以ProductServlet名称开始的通过请求分派器include()调用传递过来的请求才会调用Logging Filter。

如下代码:

<filter-mapping>  
   <filter-name>Logging Filter</filter-name>  
   <url-pattern>/products/*</url-pattern>  
   <dispatcher>FORWARD</dispatcher>  
   <dispatcher>REQUEST</dispatcher>  
</filter-mapping>  

以/products/…开始的客户端请求,以及以路径/products/…开始的通过请求分派器forward()调用传递过来的请求,都将导致Logging Filter被调用。

最后,如下代码使用特殊的servlet名字 “*”:

<filter-mapping>  
   <filter-name>All Dispatch Filter</filter-name>  
   <servlet-name>*</servlet-name>  
   <dispatcher>FORWARD</dispatcher>  
</filter-mapping>  

在按名字或按路径获取的所有请求分派器forward()调用时该代码将导致所有分派过滤器(Dispatch Filter)被调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值