java servlet3.1规范解读系列七:filter

过滤器

过滤器( Filter)是 Java 组件, 允许运行过程中改变进入资源的请求和资源返回的响应中的有效负载和 header信息。

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

 

6.1 什么是过滤器

过滤器是一种代码重用的技术,它可以改变 HTTP 请求的内容,响应,及 header 信息。过滤器通常不产生响应或像 servlet 那样对请求作出响应,而是修改或调整到资源的请求,修改或调整来自资源的响应。过滤器可以作用于动态或静态内容。这章说的动态和静态内容指的是 Web 资源。

解读:

这个地方重点说下:这个地方说出了filter的几个重要使用方法:

1: 修改http请求(头信息,请求内容)

2: 修改响应 (可以对servlet的返回做出过滤,修改)

3: 调整请求 在filter中可以跳转当前请求到其他请求

4: 做出响应 (即 请求不一定要在servlet中做出响应,在filter中,也可以返回响应)

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

■ 在执行请求之前访问资源。

■ 在执行请求之前处理资源的请求。

■ 用请求对象的自定义版本包装请求对请求的 header 和数据进行修改。

■ 用响应对象的自定义版本包装响应对响应的 header 和数据进行修改。

■ 拦截资源调用之后的调用。

■ 作用在 Servlet,一组 Servlet,或静态内容上的零个,一个或多个拦截器按指定的顺序执行。

6.1.1 过滤器组件示例

■ 验证过滤器

■ 日志记录和审计过滤器

■ 图像转换过滤器

■ 数据压缩过滤器

■ 加密过滤器

■ 词法( Tokenizing)过滤器

■ 触发资源访问事件过滤器

■ 转换 XML 内容的 XSL/T 过滤器

■ MIME-类型链过滤器

■ 缓存过滤器

解读:

这里列出了一些个常用的过滤器,当然在具体的程序设计中还可以增加其他的过滤器,比如常用的 ip访问限制过滤器,cas登录过滤器,计数过滤器 等等

 

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 的容器中仅实例化一个实例。容器提供了声明在过滤器的部署描述符的过滤器 config(译者注: FilterConfig),对 Web 应用的 ServletContext 的引用,和一组初始化参数。

解读:

1: 每个jvm的容器中仅实例化一个实例,也就是说filter是单例模式

2: 初始化动作是容器进行的,开发者不需要进行处理

3: 在初始化时会调用init方法

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

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

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

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

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

解读:

这个地方很重要了,通常情况下,在HttpServletRequest、HTTPServletResponse不能满足我们需求的情况下,我们可以自定义自己的HttpServletRequest、HTTPServletResponse,当然需要继承这两个类,通常是在filter中调用FilterChain.doFilter 中,传入我们自己封装过的HttpServletRequest、HTTPServletResponse 对象,在之后的filter和servlet中,使用的都是我们封装过后的对象,这其实是非常有用的。给了我们很大的自由发挥空间。举个例子:在字符过滤器中,我们仅仅可以设置post请求的字符集,并不能对get请求的字符集做处理,在这种情况下,我们就可以自己封装HttpServletRequest 对象,重写getParameter 方法,在取值时增加字符集转换!

 

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

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

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

6. 另外,过滤器可能抛出一个异常以表示处理过程中出错了。如果过滤器在 doFilter 处理过程中抛出

UnavailableException,容器必须停止处理剩下的过滤器链。 如果异常没有标识为永久的,它或许选择稍候重试整个链。

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

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

6.2.2 包装请求和响应

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

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

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

解读:

这个地方可以注意下,就是在filter中进行连接跳转的具体使用方法了

6.2.3 过滤器环境

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

6.2.4 在 Web 应用中配置过滤器

过滤器可以通过“ @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 和静态内容:

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

<filter-mapping>

<filter-name>Logging Filter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

当使用<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>元素来表示。

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

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

4. 使用第 106 页“错误处理”指定的错误页面机制处理匹配<url-pattern>的错误资源的请求。

可以由一个带有 ERROR 值的<dispatcher>元素表示。

5. 使用第 10 页指定的“异步处理”中的异步上下文分派机制对 web 组件使用 dispatch 调用处理请求。

可以由一个带有 ASYNC 值的<dispatcher>元素表示。

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

解读:

这个地方平常其实不太用的,主要是增加了filter的作用返回,在特别需求中可能会用到

例如:

<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 的请求将不会导致 Logging Filter 被调用,且也不会在请求分派器 forward()调用到ProductServlet 情况时,仅在以 ProductServlet 名字开头的请求分派器 include()调用时被调用。如下代码:

<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()调用时该代码将导致 All Dispatch Filter 被调用。

 

总结:

filter的特性是:

1: 容器负责实例化,会调用init方法进行初始化,可以传入一些初始化参数

2: 每个jvm实例中,对一个filter,容器仅可以实例化一个实例,filter是单例状态

3: filter中调用doFilter时,可以传入自定义的HTTPServletRequest,HTTPServletResponse,FilterChain 对象,进行自己的处理

4: 在filter中可以修改请求,修改响应,做出响应,请求跳转等操作

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值