Servlet API 非常久曾经就已成为企业应用开发的基石,而 Servlet 过滤器则是对 J2EE 家族的相对较新的补充。在 J2EE 探索者 系列文章的最后一篇中,作者 Kyle Gabhart 将向您介绍 Servlet 过滤器体系结构,定义过滤器的很多应用,并指导您完毕典型过滤器实现的三个步骤。他还会透露 bean
的一些激动人心的变化,估计刚公布的 Java Servlet 2.4 规范会引入这些变化。



Servlet 过滤器是可插入的 Web 组件,它同意我们实现 Web 应用程序中的预处理和后期处理逻辑。过滤器支持 servlet 和 JSP 页面的基本请求处理功能,比方日志记录、性能、安全、会话处理、XSLT 转换,等等。 过滤器最初是随 Java Servlet 2.3 规范公布的,近期定稿的 2.4 规范对它进行了重大升级。在这 J2EE 探索者 系列文章的最后一篇中,我将向您介绍 Servlet 过滤器的基础知识 —— 比方整体的体系结构设计、实现细节,以及在 J2EE Web 应用程序中的典型应用,还会涉及一些估计最新的
Servlet 规范将会提供的扩展功能。



Servlet 过滤器是什么?

Servlet 过滤器是小型的 Web 组件,它们拦截请求和响应,以便查看、提取或以某种方式操作正在客户机和server之间交换的数据。过滤器是通常封装了一些功能的 Web 组件,这些功能尽管非常重要,可是对于处理客户机请求或发送响应来说不是决定性的。典型的样例包含记录关于请求和响应的数据、处理安全协议、管理会话属性, 等等。过滤器提供一种面向对象的模块化机制,用以将公共任务封装到可插入的组件中,这些组件通过一个配置文件来声明,并动态地处理。



Servlet 过滤器中结合了很多元素,从而使得过滤器成为独特、强大和模块化的 Web 组件。也就是说,Servlet 过滤器是:



声明式的:过滤器通过 Web 部署描写叙述符(web.xml)中的 XML 标签来声明。这样同意加入和删除过滤器,而无需修改不论什么应用程序代码或 JSP 页面。



动态的:过滤器在执行时由 Servlet 容器调用来拦截和处理请求和响应。



灵活的:过滤器在 Web 处理环境中的应用非常广泛,涵盖诸如日志记录和安全等很多最公共的辅助任务。过滤器还是灵活的,由于它们可用于对来自客户机的直接调用运行预处理和后期处 理,以及处理在防火墙之后的 Web 组件之间调度的请求。最后,能够将过滤器链接起来以提供必需的功能。



模块化的:通过把应用程序处理逻辑封装到单个类文件里,过滤器从而定义了可easy地从请求/响应链中加入或删除的模块化单元。



可移植的:与 Java 平台的其它很多方面一样,Servlet 过滤器是跨平台和跨容器可移植的,从而进一步支持了 Servler 过滤器的模块化和可重用本质。



可重用的:归功于过滤器实现类的模块化设计,以及声明式的过滤器配置方式,过滤器能够easy地跨越不同的项目和应用程序使用。



透明的:在请求/响应链中包含过滤器,这样的设计是为了补充(而不是以不论什么方式替代)servlet 或 JSP 页面提供的核心处理。因而,过滤器能够依据须要加入或删除,而不会破坏 servlet 或 JSP 页面。

所以 Servlet 过滤器是通过一个配置文件来灵活声明的模块化可重用组件。过滤器动态地处理传入的请求和传出的响应,而且无需改动应用程序代码就能够透明地加入或删除它 们。最后,过滤器独立于不论什么平台或者 Servlet 容器,从而同意将它们easy地部署到不论什么相容的 J2EE 环境中。



在接下来的几小节中,我们将进一步考察 Servlet 过滤器机制的整体设计,以及实现、配置和部署过滤器所涉及的步骤。我们还将探讨 Servlet 过滤器的一些实际应用,最后简要考察一下模型-视图-控制器(MVC)体系结构中包括的 Servlet 过滤器,从而结束本文的讨论。



Servlet 过滤器体系结构

正如其名称所暗示的,Servlet 过滤器 用于拦截传入的请求和/或传出的响应,并监视、修改或以某种方式处理正在通过的数据流。过滤器是自包括、模块化的组件,能够将它们加入到请求/响应链中, 或者在无需影响应用程序中其它 Web 组件的情况下删除它们。过滤器仅仅仅是修改请求和响应的执行时处理,因而不应该将它们直接嵌入 Web 应用程序框架,除非是通过 Servlet API 中良好定义的标准接口来实现。



Web 资源能够配置为没有过滤器与之关联(这是默认情况)、与单个过滤器关联(这是典型情况),甚至是与一个过滤器链相关联。那么过滤器到底做什么呢? 像 servlet 一样,它接受请求并响应对象。然后过滤器会检查请求对象,并决定将该请求转发给链中的下一个组件,或者中止该请求并直接向客户机发回一个响应。假设请求被 转发了,它将被传递给链中的下一个资源(还有一个过滤器、servlet 或 JSP 页面)。在这个请求设法通过过滤器链并被server处理之后,一个响应将以相反的顺序通过该链发送回去。这样就给每一个过滤器都提供了依据须要处理响应对象的机
会。



当过滤器在 Servlet 2.3 规范中首次引入时,它们仅仅能过滤 Web 客户机和客户机所訪问的指定 Web 资源之间的内容。假设该资源然后将请求调度给其它 Web 资源,那就不能向幕后托付的不论什么请求应用过滤器。2.4 规范消除了这个限制。Servlet 过滤器如今能够应用于 J2EE Web 环境中存在请求和响应对象的不论什么地方。因此,Servlet 过滤器能够应用在客户机和 servlet 之间、servlet 和 servlet 或 JSP 页面之间,以及所包含的每一个 JSP 页面之间。这才是我所称的强大能力和灵活性!



实现一个 Servlet 过滤器

他们说“好事多磨”。我不知道“他们”指的是谁,或者这句古老的谚语到底有多真实,可是实现一个 Servlet 过滤器的确要经历三个步骤。首先要编写过滤器实现类的程序,然后要把该过滤器加入到 Web 应用程序中(通过在 Web 部署描写叙述符 /web.xml 中声明它),最后要把过滤器与应用程序一起打包并部署它。我们将具体研究这当中的每一个步骤。



1. 编写实现类的程序

过滤器 API 包括 3 个简单的接口(又是数字 3!),它们整洁地嵌套在 javax.servlet 包中。那 3 个接口各自是 Filter、FilterChain 和 FilterConfig。从编程的角度看,过滤器类将实现 Filter 接口,然后使用这个过滤器类中的 FilterChain 和 FilterConfig 接口。该过滤器类的一个引用将传递给 FilterChain 对象,以同意过滤器把控制权传递给链中的下一个资源。FilterConfig 对象将由容器提供给过滤器,以同意訪问该过滤器的初始化数据。



为了与我们的三步模式保持一致,过滤器必须运用三个方法,以便全然实现 Filter 接口:



init():这种方法在容器实例化过滤器时被调用,它主要设计用于使过滤器为处理做准备。该方法接受一个 FilterConfig 类型的对象作为输入。



doFilter():与 servlet 拥有一个 service() 方法(这种方法又调用 doPost() 或者 doGet())来处理请求一样,过滤器拥有单个用于处理请求和响应的方法——doFilter()。这种方法接受三个输入參数:一个 ServletRequest、response 和一个 FilterChain 对象。



destroy():正如您想像的那样,这种方法运行不论什么清理操作,这些操作可能须要在自己主动垃圾收集之前进行。展示了一个很easy的过滤器,它跟踪满足一个客户机的 Web 请求所花的大致时间。



一个过滤器类实现

  1. import javax.servlet.*;
  2. import java.util.*;
  3. import java.io.*;
  4. public class TimeTrackFilter implements Filter {
  5. private FilterConfig filterConfig = null;
  6. public void init(FilterConfig filterConfig)
  7. throws ServletException {
  8. this.filterConfig = filterConfig;
  9. }
  10. public void destroy() {
  11. this.filterConfig = null;
  12. }
  13. public void doFilter( ServletRequest request,
  14. ServletResponse response, FilterChain chain )
  15. throws IOException, ServletException {
  16. Date startTime, endTime;
  17. double totalTime;
  18. startTime = new Date();
  19. // Forward the request to the next resource in the chain
  20. chain.doFilter(request, wrapper);
  21. // -- Process the response -- \\
  22. // Calculate the difference between the start time and end time
  23. endTime = new Date();
  24. totalTime = endTime.getTime() - startTime.getTime();
  25. totalTime = totalTime / 1000; //Convert from milliseconds to seconds
  26. StringWriter sw = new StringWriter();
  27. PrintWriter writer = new PrintWriter(sw);
  28. writer.println();
  29. writer.println("===============");
  30. writer.println("Total elapsed time is: " + totalTime + " seconds." );
  31. writer.println("===============");
  32. // Log the resulting string
  33. writer.flush();
  34. filterConfig.getServletContext().
  35. log(sw.getBuffer().toString());
  36. }
  37. }

复制代码

这个过滤器的生命周期非常easy,无论如何,我们还是研究一下它吧:



初始化

当容器第一次载入该过滤器时,init() 方法将被调用。该类在这种方法中包括了一个指向 FilterConfig 对象的引用。我们的过滤器实际上并不须要这样做,由于当中没有使用初始化信息,这里仅仅是出于演示的目的。



过滤

过滤器的大多数时间都消耗在这里。doFilter() 方法被容器调用,同一时候传入分别指向这个请求/响应链中的 ServletRequest、ServletResponse 和 FilterChain 对象的引用。然后过滤器就有机会处理请求,将处理任务传递给链中的下一个资源(通过调用 FilterChain 对象引用上的 doFilter()方法),之后在处理控制权返回该过滤器时处理响应。





析构

容器紧跟在垃圾收集之前调用 destroy() 方法,以便可以运行不论什么必需的清理代码。



2. 配置 Servlet 过滤器

过滤器通过 web.xml 文件里的两个 XML 标签来声明。<filter> 标签定义过滤器的名称,而且声明实现类和 init() 參数。<filter-mapping> 标签将过滤器与 servlet 或 URL 模式相关联。



摘自一个 web.xml 文件,它展示了怎样声明过滤器的包括关系:



在 web.xml 中声明一个过滤器

  1. <filter>
  2. <filter-name>Page Request Timer</filter-name>
  3. <filter-class>TimeTrackFilter</filter-class>
  4. </filter>
  5. <filter-mapping>
  6. <filter-name>Page Request Timer</filter-name>
  7. <servlet-name>Main Servlet</servlet-name>
  8. </filter-mapping>
  9. <servlet>
  10. <servlet-name>Main Servlet</servlet-name>
  11. <servlet-class>MainServlet</servlet-class>
  12. </servlet>
  13. <servlet-mapping>
  14. <servlet-name>Main Servlet</servlet-name>
  15. <url-pattern>/*</url-pattern>
  16. </servlet-mapping>

复制代码

上 面的代码演示样例声明了一个过滤器("Page Request Timer"),并把它映射到一个 servlet("Main Servlet")。然后为该 servlet 定义了一个映射,以便把每一个请求(由通配符指定)都发送到该 servlet。这是控制器组件的典型映射声明。您应该注意这些声明的顺序,由于千万不能背离这些元素的顺序。



3. 部署 Servlet 过滤器

其实,与 Web 应用程序一起部署过滤器绝对不涉及不论什么复杂性。仅仅需把过滤器类和其它 Web 组件类包含在一起,并像您通常所做的那样把 web.xml 文件(连同过滤器定义和过滤器映射声明)放进 Web 应用程序结构中,servlet 容器将处理之后的其它全部事情。



过滤器的很多应用

您在 J2EE Web 应用程序中利用过滤器的能力,仅受到您自己的创造性和应用程序设计本领的限制。在适合使用装饰过滤器模式或者拦截器模式的不论什么地方,您都能够使用过滤器。过滤器的一些最普遍的应用例如以下:



载入:对于到达系统的全部请求,过滤器收集诸如浏览器类型、一天中的时间、转发 URL 等相关信息,并对它们进行日志记录。



性能:过滤器在内容通过线路传来并在到达 servlet 和 JSP 页面之前解压缩该内容,然后再取得响应内容,并在将响应内容发送到客户机机器之前将它转换为压缩格式。



安全:过滤器处理身份验证令牌的管理,并适当地限制安全资源的訪问,提示用户进行身份验证和/或将他们指引到第三方进行身份验证。过滤器甚至能够管理訪问 控制列表(Access Control List,ACL),以便除了身份验证之外还提供授权机制。将安全逻辑放在过滤器中,而不是放在 servlet 或者 JSP 页面中,这样提供了巨大的灵活性。在开发期间,过滤器能够关闭(在 web.xml 文件里凝视掉)。在生产应用中,过滤器又能够再次启用。此外还能够加入多个过滤器,以便依据须要提高安全、加密和不可拒绝的服务的等级。



会话处理:将 servlet 和 JSP 页面与会话处理代码混杂在一起可能会带来相当大的麻烦。使用过滤器来管理会话能够让 Web 页面集中精力考虑内容显示和托付处理,而不必操心会话管理的细节。



XSLT 转换:无论是使用移动client还是使用基于 XML 的 Web 服务,无需把逻辑嵌入应用程序就在 XML 语法之间运行转换的能力都绝对是无价的。



使过滤器适应 MVC 体系结构

模型-视图-控制器(Model-View-Controller,MVC)体系结构是一个有效的设计,它如今已作为最重要的设计方法学,整合到了诸如 Jakarta Struts 和 Turbine 等大多数流行的 Web 应用框架中。过滤器旨在扩充 MVC 体系结构的请求/响应处理流。无论请求/响应发生在客户机和server之间,还是发生在server上的其它组件之间,过滤器在处理流中的应用都是同样的。从 MVC 的观点看,调度器组件(它或者包含在控制器组件中,或者配合控制器组件工作)把请求转发给适当的应用程序组件以进行处理。这使得控制器层成为包含
Servlet 过滤器的最佳位置。通过把过滤器放在控制器组件本身的前面,过滤器能够应用于全部请求,或者通过将它放在控制器/调度器与模型和控制器之间,它能够应用于 单独的 Web 组件。



MVC 体系结构广为传播,并具有良好的文档。请通过 參考资料 中的链接了解关于 MVC 和 MVC 体系结构中的 Servlet 实现的很多其它信息。



结束语

尽管过滤器才出现几年时间,但它们本身已作为一个关键组件嵌入到了全部敏捷的、面向对象的 J2EE Web 应用程序中。本文向您介绍了 Servlet 过滤器的使用。本文讨论了过滤器的高级设计,比較了当前规范(2.4)和曾经(2.3)的模型,讲述了实现过滤器所涉及的精确步骤,以及怎样在 Web 应用程序中声明过滤器,然后与应用程序一起部署它。本文还阐述了 Servlet 过滤器的一些最普遍应用,并提到了过滤器怎样适应传统的 MVC 体系结构。



这是 J2EE 探索者 系列的最后一篇文章。我们在年初通过粗略研究 Enterprise JavaBean 组件来開始我们的旅程,并提到了何时使用这些组件才真正有意义,以及何时这些组件才会变得大材小用的问题。然后我们将目光转向了 Web 层,绘制了一条通过 Servlet、JSP 页面、JavaBean 技术以及 Java Servlet API 中的无数选择和功能的路径。在这个系列文章中与您一起艰苦跋涉真是一件快乐的事情。我享受着编写这个系列文章的乐趣,而且我从大家的反馈中知道,这对您也 是一个非常有价值的过程。

Java 过滤器的作用

http://www.ossez.com/forum.php?mod=viewthread&tid=13396&fromuid=426

Java 过滤器的作用的更多相关文章

  1. cookie、session和java过滤器

    基础知识理解: cookie.session和过滤器通常都是用在web应用中,cookie和session用来保存一定的数据,过滤器Filter则是在浏览器发出请求之后,而后台执行特定的请求之前发生一 ...

  2. java 接口的作用和好处

    1.java 接口的作用 http://blog.csdn.net/hack_bug/article/details/7634737 2.一位Java大牛的回答 很多JAVA初级程序员对于接口存在的意 ...

  3. Java 动态代理作用是什么?

    Java 动态代理作用是什么?   1 条评论 分享   默认排序按时间排序 19 个回答 133赞同反对,不会显示你的姓名 Intopass 程序员,近期沉迷于动漫ING 133 人赞同 ① 首先你 ...

  4. Java关键字及其作用

    Java关键字及其作用 一. 关键字总览 访问控制 private protected public             类,方法和变量修饰符 abstract class extends fin ...

  5. Java过滤器处理Ajax请求,Java拦截器处理Ajax请求,拦截器Ajax请求

    Java过滤器处理Ajax请求,Java拦截器处理Ajax请求,拦截器Ajax请求 >>>>>>>>>>>>>>&g ...

  6. 【Java学习笔记之一】java关键字及作用

    Java关键字及其作用 一. 总览: 访问控制 private protected public 类,方法和变量修饰符 abstract class extends final implements ...

  7. JAVA关键字及作用

    ---恢复内容开始--- Java关键字及其作用 一. 总览: 访问控制 private protected public 类,方法和变量修饰符 abstract class extends fina ...

  8. java过滤器(过滤器排序)

    java过滤器(过滤器排序) 定义过滤器顺序是很简单的:匹配请求的过滤器将按照它们出现在部署描述符或者编程式配置中的顺序添加到过滤器链中(记住,如果同时再部署描述符或者编程式配置中设置了一些过滤器,那 ...

  9. java内部类的作用

    java内部类的作用 定义: 放在一个类的内部的类我们就叫内部类. 二. 作用: 1.内部类可以很好的实现隐藏 一般的非内部类,是不允许有 private 与protected权限的,但内部类可以 2 ...

随机推荐

  1. 王垠:Lisp 已死,Lisp 万岁!

    王垠:Lisp 已死,Lisp 万岁!_IT新闻_博客园 王垠:Lisp 已死,Lisp 万岁!

  2. Linux $( )与${ }的区别

    初学者也许会遇到这个问题~ $( )的用途和反引号` `一样,用来表示优先执行的命令,比如ls -l $(locate build.xml),表示先找到build.xml的位置,然后再列出详细信息. ...

  3. C++内存管理学习笔记(6)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  4. 14.3.3 Locks Set by Different SQL Statements in InnoDB 不同的SQL语句在InnoDB里的锁设置

    14.3.3 Locks Set by Different SQL Statements in InnoDB 不同的SQL语句在InnoDB里的锁设置 locking read, 一个UPDATE,或 ...

  5. vdsm的SSL证书验证过程

    1. Copy the VDSM certificate of the RHEV-H(Red Hat Enterprise Virtualization Hypervisor ) host to th ...

  6. <Win32_17>集音频和视频播放功能于一身的简易播放器

    前段时间,在学习中科院杨老师的教学视频时,他说了一句话: "我很反对百八十行的教学程序,要来就来一个完整的程序" 对此,我很是赞同.所谓真刀真枪的做了,你才会发现其中的奥秘——然而 ...

  7. 跟我一起学extjs5(13--运行菜单命令在tabPanel中显示模块)

    跟我一起学extjs5(13--运行菜单命令在tabPanel中显示模块)         上面设计好了一个模块的主界面,以下通过菜单命令的运行来把这个模块增加到主界面其中. 在MainModule. ...

  8. Wi-Fi万能钥匙:说是破解,其实有危险(转)

    Wi-Fi 万能钥匙如此危险,怎样做才能让这种可能严重侵害公众利益的 app 在中国消失? 这个“钥匙”为什么能够破解 Wi-Fi?它真的是“破解” Wi-Fi 吗?两年前我就有这个疑问了,原谅我对一 ...

  9. C语言里为何会有“2+2=5”的结果

    写这篇原创文章是由于看到了极客中的一篇文章<有趣各种编程语言实现2+2=5>,当中C语言是这样实现的: int main() { char __func_version__[] = &qu ...

  10. DIV固定在页面某个位置,不随鼠标滚动而滚动

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3c.org ...