起源之路——Servlet(五)

过滤器

什么是过滤器

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

简而言之就是可以通过过滤器来改变进入资源的请求中的和资源返回的响应中的数据。它是一种代码重用技术,可以转换HTTP请求的内容,响应及头信息。过滤器通常不产生响应或像Servlet那样对请求作出响应,而是修改或调整到资源的请求或是来自资源的响应。就像进入小区之前的保安,对进出的人进行一定的操作,但进出的人的核心目标与保安无关,只是为了出入小区而已。
通常过滤器的功能有如下几种类型:

  • 执行请求前访问资源
  • 执行请求前处理资源的请求
  • 用请求对象的自定义版本包装请求(即通过filter对请求的header和数据进行加工)
  • 用响应对象的自定义版本包装响应(即通过filter对响应的header和数据进行加工)
  • 拦截资源调用之后的调用
  • 作用在一个或一组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 //MIME-TYPE 链过滤器
  • Caching filters //缓存过滤器

过滤器的生命周期

开发者通过实现javax.servlet.Filter接口并提供一个空参构造器来创建过滤器。Filter在部署描述符中通过<filter>标签元素来声明。一个或一组过滤器可以通过在部署描述符中定义<filter-mapping>子标签元素来为调用配置。同时也可以使用servlet的逻辑视图名把过滤器映射到一个特定的servlet,或使用URL模式把一组servlet和静态内容资源映射到过滤器。

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

部署描述符中声明的每个<filter>在每个JVM容器中仅实例化一个实例。容器提供声明在过滤器部署描述符中的过滤器config,即FilterConfig,还有对Web应用的ServletContext的引用和一组初始化参数。

当容器接收到传入的请求时,将会获取过滤器列表中的第一个过滤器并调用器doFilter方法,传入ServletRequest对象、ServletResponse对象和一个它将要使用的FilterChain(过滤器链)对象的引用。doFilter方法通常被实现为如下形式或它的子集:

  1. 检查请求头
  2. 为了修改请求的头或数据,自定义ServletRequest或HTTPServletRequest实现包装请求对象
  3. 自定义ServletResponse或HTTPServletResponse实现包装传入的响应对象用于修改响应的头和数据
  4. 调用过滤器链中的下一个实体,下一个实体可能是另一个过滤器,如果当前过滤器是最后一个过滤器,下一个实体是目标Web资源。调用FilterChain对象的doFilter方法将影响下一个实体的调用,并且传入的它被调用时的请求或响应,或者传入它可能已经创建的包装版本。由容器提供的FilterChain的doFilter方法的实现,必须找出过滤器链中的下一个实体并调用它的doFilter方法,传入适当的请求和响应对象。另外,过滤器链可以通过不调用下一个实体来阻止请求,当前离开的过滤器负责填充响应对象。service方法必须和应用到servlet的所有过滤器运行在同一个线程中。
  5. 过滤器链中的下一个过滤器调用之后,过滤器可能会检查响应的头。
  6. 如果过滤器在doFilter处理过程中抛出UnavailableException异常,容器必须停止处理剩下的过滤器链,如果异常没有被表示为永久的,或许选择稍后重试整个链。
  7. 当链中最后的过滤器被调用,下一个实体访问的是链最后的目标Servlet或资源。
  8. 容器把服务中的过滤器实例移除之前,容器必须先调用过滤器的destroy方法来以便于过滤器释放资源并执行其他的清理工作。

包装请求和响应

过滤器的核心概念是包装请求或响应,以便于覆盖行为执行过滤任务。开发者不仅可以覆盖请求和响应对象上已有的方法,也能扩展新的API以适用于对过滤器链中剩下的过滤器或目标Web资源做特殊的过滤任务。例如,用更高级杯的输出对象(output stream或writer)来扩展响应对象或是允许DOM对象写回客户端API。为了支持这种风格的过滤器,容器必须支持如下要求:

  • 当过滤器调用容器的过滤器链实现的doFilter方法时,容器必须确保请求和响应对象传到过滤器链中的下一个实体,如果当前过滤器是最后一个过滤器,将传入目标Web资源,并且与嗲用过滤器传入的doFilter方法的对象是一样的。
  • 当调用者包装请求或响应对象时,对包装对象的要求同样适用于从servlet或过滤器到RequestDispatcher.forward或RequestDispatcher.include的调用。在这种情况下,调用servlet看到的请求和响应对象与调用servlet或过滤器传入的包装对象必须是一样的。

过滤器环境

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

过滤器和RequestDispatcher

从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. 使用“错误处理”指定的错误页面机制处理匹配<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 将在初始请求分派和恢复请求时被调用。

如下代码表示客户端到 ProductServlet 的请求将不会导致 Logging Filter 被调用,且也不会在请求分派器 forward() 调用到 ProductServlet 情况时,仅在以 ProductServlet 名字开头的请求分派器 include() 调用时被调用。:

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

如下代码客户端以/products/…开始的请求,或在以路径/products/…开始的请求分派器 forward() 调用情况时,将导致 Logging Filter 被调用:

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

如下代码使用特殊的 servlet 名字 “*”,在按名字或按路径获取的所有请求分派器 forward() 调用时该代码将导致All Dispatch Filter 被调用。:

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

参考链接: 来自waylau翻译的《Java Servlet 3.1 规范》
自己使用整理收集,如有侵权 请联系删除!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值