Servlet过滤器简介

很早之前,Servlet API就已成为企业应用开发的重要工具。现在,Servlet中的过滤器和监听器功能则是对J2EE体系的一个补充。过滤器使得Servlet开发者能够在请求到达Servlet之前截取请求,在Servlet处理请求之后修改应答;而Servlet监听器可以监听客户端的请求、服务端的操作,通过监听器,可以自动激发一些操作,如监听应用的启动和停止等。本章将接着介绍Servlet过滤器和监听器的相关知识以及在实际开发中如何使用这些技术。

本章重点:

  ● Servlet过滤器简介

  ● Servlet过滤器的实现

  ● Servlet过滤文本信息

  ● Servlet监听器简介

 Servlet过滤器简介

Servlet过滤器是小型的Web组件,能拦截请求和响应,以便查看、提取或以某种方式操作正在客户机和服务器之间交换的数据。过滤器是封装了一些功能的Web组件,这些功能虽然很重要,但是对于处理客户机请求或发送响应来说不是决定性的。典型的例子包括记录关于请求和响应的数据、处理安全协议、管理会话属性等。

一个过滤器可以被关联到任意多个资源,一个资源也可以关联到任意多个过滤器。

 Web资源与过滤器相关联

关联到同一个资源的过滤器形成一个过滤器链。例如,对S2进行访问时,F1、F2、F3就形成了一个过滤器链,客户要访问资源S2,就要经过F1过滤器,然后是F2、F3,最后才是要访问的资源。如果在经过上面这3个过滤器时出现了错误或者被禁止访问,客户的请求就无法到达资源S2。在Servlet过滤器中,这个过滤器传递的过程是通过FilterChain.dofilter()方法实现的,当过滤器链到达末尾时,这个方法调用请求的资源。建立一个过滤器,一般需要下面4个步骤:

(1)建立一个实现Filter接口的类。这个类需要3个方法,即doFilter、init和destroy。doFilter方法包含主要的过滤代码(见第(2)步),在init方法中进行一些初始化的设置,而destroy方法进行清除过滤器占用的资源。

(2)在doFilter方法中放入过滤行为。doFilter方法的第一个参数为ServletRequest对象,为过滤器提供了对进入的信息(包括表单数据、cookie和HTTP请求头)的完全访问。第二个参数为ServletResponse,通常在简单的过滤器中忽略此参数。最后一个参数为FilterChain,用来调用过滤器链中下一个过滤器或者在到达过滤器末尾时调用Servlet或JSP页。

(3)调用FilterChain对象的doFilter方法。Filter接口的doFilter方法将一个FilterChain对象作为它的一个参数。在调用此对象的doFilter方法时,激活下一个相关的过滤器。如果没有另一个过滤器与Servlet或JSP页面关联,则Servlet或JSP页面被激活。

(4)对相应的Servlet和JSP页面注册过滤器。在部署描述符文件(web.xml)中使用filter和filter-mapping元素对需要进行过滤的资源进行配置。

  实现一个Servlet过滤器

Servlet过滤器API包含3个简单的接口,即Filter、FilterChain和FilterConfig,它们位于javax.servlet包中。从编程的角度看,过滤器类将实现Filter接口,然后使用这个过滤器类中的FilterChain和FilterConfig接口。该过滤器类的一个引用将传递给FilterChain对象,以允许过滤器将控制权传递给过滤器链中的下一个过滤器或者资源。FilterConfig对象将由容器提供给过滤器,以允许访问该过滤器的初始化数据。

14.2.1  编写实现类的程序

下面是一个简单的Servlet过滤器的例子,其中将获取一个Web服务器给客户提供服务需要的时间,也就是响应时间。

【本节示例参考:资料光盘\源代码\第14章\14-2\Servlet.java】

【实例14-1】这是一个实现类的程序,主要是使用过滤器处理请求,在处理时可能要耗费很多    时间。

程序14-1  Servlet.java

01  package cn.ac.ict.Filter;

02  import java.io.IOException;

03  import java.io.PrintWriter;

04  import java.io.StringWriter;

05  import java.util.Date;

06  import javax.servlet.*;

07  public class ResponseTimeFilter implementsFilter {

08      private FilterConfigfilterConfig = null;

09      public void init(FilterConfigconfig) throws ServletException {//初始化函数

10         this.filterConfig = config;

11      }

12      public voiddoFilter(ServletRequest request, ServletResponse response,FilterChain chain)throws IOException, ServletException {

13          DatestartTime, endTime;

14          doubletotalTime;

15          startTime= new Date();

16         StringWriter sw = new StringWriter();

17         PrintWriter writer = new PrintWriter(sw);

18         writer.println();

19         writer.println("ResponseTimeFilter Before call chain.doFilter");

20         chain.doFilter(request, response);                   //将请求转发给过滤器链中下一个资源

21          //下面是资源的响应时间

22          //下面计算开始时间和结束时间的差,也就是响应时间

23         endTime = new Date();

24         totalTime = endTime.getTime() - startTime.getTime();

25         totalTime = totalTime / 1000; //将毫秒时间转换为以秒为单位

26         writer.println("ResponseTimeFilter Before call chain.doFilter");

27         writer.println("===============");

28         writer.println("Total elapsed time is: " + totalTime + "seconds." );

29         writer.println("===============");

30  filterConfig.getServletContext().log(sw.getBuffer().toString());   //将信息输出到日志中

31         writer.flush();

32      }

33      public void destroy() {

34         this.filterConfig = null;

35      }

36      public FilterConfiggetFilterConfig() {

37          returnnull;

38      }

39      public voidsetFilterConfig(FilterConfig config) {

40      }

41  }

【深入学习】当容器第一次加载该过滤器时,init()方法将被调用。该类在这个方法中包含了一个指向FilterConfig对象的引用。过滤器的大多数时间都消耗在执行过滤操作上,第20行中的doFilter()方法被容器调用,同时传入分别指向这个请求/响应链中的ServletRequest、ServletResponse和FilterChain对象的引用。然后过滤器就有机会处理请求,将处理任务传递给链中的下一个资源(通过调用FilterChain对象引用上的doFilter()方法),之后在处理控制权返回该过滤器时处理响应。

14.2.2  配置发布Servlet过滤器

发布Servlet过滤器时,必须在web.xml文件中加入和元素,其中,元素用来定义一个过滤器,在本例中使用了如下代码:

       

           Request Response Time

       cn.ac.ict.Filter.ResponseTimeFilter

       

以上代码中,指定过滤器的名字,指定过滤器的完整类名,也可以在元素中内嵌元素来为过滤器提供初始化参数。

元素用于将过滤器和URL关联,本例中的代码如下:

       

           Request Response Time

           /ShowCounter.jsp

          

当客户请求的URL和指定的URL相匹配时,就会触发ResponseTimeFilter过滤器,这里的元素的值和元素中的值应该相同。

说明:如果希望Servlet过滤器能过滤所有的客户请求,可以将的值设置为/*。

准备好上面的文件后,按照如下步骤即可发布这个Servlet过滤器。

(1)编译ResponseTimeFilter类,编译时,需要将Java Servlet API的包servlet-api.jar放到类路径中,编译后的类放到本章Web应用的WEB-INF\classes目录下,并且目录结构要与包的结构一致。

(2)在web.xml文件中按照上面说明的方法配置这个过滤器。

(3)为了观察这个过滤器的日志,应确保在server.xml文件的localhost对应的host元素中配置了如下的logger元素:

       

                directory="logs"  prefix="localhost_log."suffix=".txt"

           timestamp="true"/>

以上内容在默认情况下都是配置好的。

(4)启动Tomcat,在浏览器地址栏中输入http://localhost:8080/14/ShowCounter.jsp,这时在\logs\localhost_log.2009-05-24.txt文件中会生成如下的日志信息:

2009-05-26 23:03:42 StandardContext[/15]

ResponseTimeFilter Before call chain.doFilter

ResponseTimeFilter Before call chain.doFilter

===============

Total elapsed time is: 0.0 seconds.

===============

说明:本例中访问的ShowCounter.jsp页面在本章的监听器部分介绍,如果读者不想跳跃着查看这个文件,可以尝试修改过滤器关联的URL,使得它可以过滤读者指定的资源。

14.3  ServletRequest和ServletResponse的包装类

Servlet过滤器能够对客户的请求和响应进行包装,并根据自定义的行为对请求和响应进行修改,这就是通过使用ServletRequest和ServletResponse的包装类——HttpServletRequestWrapper和HttpServletResponseWrapper类来实现的。这两个类提供了所有request和response方法的默认实现,并且默认地代理了所有对原有request和response的调用。这也意味着,要改变一个方法的行为只需要从封装类继承并重新实现一个方法即可。

  ● ServletRequestWrapper类:是ServletRequest的包装类,实现了ServletRequest接口,并对其方法提供了默认实现。

  ● ServletResponseWrapper类:是ServletResponse的包装类,实现了ServletResponse接口,并对其方法提供了默认实现。

14.4  用Servlet过滤器过滤文本信息

在14.3节中简单介绍了ServletRequest和ServletResponse的包装类,它们在过滤器设计中会经常被用到。本节将介绍一个使用HttpServletResponseWrapper类的例子,客户输入的内容会被检查,如果存在不允许出现的信息,就会被过滤掉,而被替换成其他的字符。在这个例子中,客户输入用户名并发布自己的留言信息,然后提交给服务器,服务器再将这些信息反馈给客户。如果客户的留言中有指定的字符串,就会调用过滤器将这个字符串替换掉。

14.4.1  输出流管理类

在Servlet输出时可以选择不同的输出流,将它们集合在一起,即可很容易地对这些流进行管理,使用时也会更方便。如何使用输出流管理类,可以看下面的代码。

【本节示例参考:资料光盘\源代码\第14章\14-4\ByteArrayPrintWriter.java】

【实例14-2】将输出流进行方法的实例化,方便以后程序的调用。

程序14-2  ByteArrayPrintWriter.java

01  package cn.ac.ict;

02  import java.io.ByteArrayOutputStream;

03  import java.io.PrintWriter;

04  import javax.servlet.ServletOutputStream;

05  public class ByteArrayPrintWriter {

06      private ByteArrayOutputStreambaos = new ByteArrayOutputStream();

07      private PrintWriter pw = newPrintWriter(baos);

08      private ByteArrayOutputStreamsos = new ByteArrayOutputStream();

09      publicByteArrayPrintWriter(ByteArrayOutputStream aos) {

10          this.baos= aos;

11      }

12      //一个为空的构造方法

13      public ByteArrayPrintWriter(){

14      }

15      //使用字符输出流时调用获取输出流

16      public PrintWritergetWriter() {

17          return pw;

18      }

19      //使用字节输出流时调用获取输出流

20      public ByteArrayOutputStreamgetStream() {

21          returnsos;

22      }

23      byte[] toByteArray() {

24          returnbaos.toByteArray();

25      }

26  }

【深入学习】在第6、7、8行共定义了3个对象,一个是用字符流输出的,一个是用字节流输出的,但是它们最终都套接到类型为ByteArrayOutputStream的对象上,这个对象为它们提供了原始输出数据。

14.4.2  编写Servlet过滤器

前面编写了需要使用的辅助类,接下来即可开发进行实际过滤功能的文本过滤器。

【实例14-3】如何实现文本过滤器,代码(TextModifierFilter.java)如下:

程序14-3  TextModifierFilter.java

01  package cn.ac.ict;

02  import java.io.IOException;

03  import java.io.PrintWriter;

04  import java.util.Locale;

05  import javax.servlet.Filter;

06  import javax.servlet.FilterChain;

07  import javax.servlet.FilterConfig;

08  import javax.servlet.ServletException;

09  import javax.servlet.ServletOutputStream;

10  import javax.servlet.ServletRequest;

11  import javax.servlet.ServletResponse;

12  import javax.servlet.http.HttpServletRequest;

13  import javax.servlet.http.HttpServletResponse;

14  importcom.sun.xml.internal.ws.util.ByteArrayBuffer;

15  public class TextModifierFilter implementsFilter, ServletResponse {

16      private FilterConfigfilterConfig;

17      private String searchStr;

18      private String replaceStr;

19      public void init(FilterConfigconfig) throws ServletException {

20         this.filterConfig = config;

21      //初始化要查找的字符串和替换后的字符串的值

22         this.searchStr = "search";

23         this.replaceStr = "replace";

24      }

25      public voiddoFilter(ServletRequest request, ServletResponse response,FilterChain chain)throws IOException, ServletException {

26         HttpServletRequest hsr = (HttpServletRequest)request;

27          finalHttpServletResponse resp = (HttpServletResponse)response;

28          finalByteArrayBuffer pw = new ByteArrayBuffer();

29  //构造响应包装类

30         TextModifierFilter tmrw = new TextModifierFilter();

31     filterConfig.getServletContext().log("TextModifierFilter:Before callchain.doFilter");

32  //将请求转发给Servlet,并获取响应

33         chain.doFilter(request,tmrw);

34  //下面的代码检查响应并对需要替换的字符进行替换

35  //将响应转化为byte数组

36          byte[]bytes = pw.toByteArray();

37          if (bytes== null || (bytes.length == 0)) {

38         filterConfig.getServletContext().log("No content!");

39          }

40  //将其转换为字符串便于查找

41          Stringcontent  = new String(bytes);

42          Stringsearchcontent = (new String(bytes)).toLowerCase();

43  //查找指定字符出现的第一个位置

44          int endPos= searchcontent.indexOf(this.searchStr);

45          StringreturnStr;

46  //响应中包含这样一个字符串

47         if(endPos!=-1){

48             String front_part_Str = content.substring(0,endPos);

49            returnStr=front_part_Str ""+this.replaceStr+""+content.substring(endPos + this.searchStr.length());

50          }else{

51  //响应中不包含这样一个字符串

52             returnStr = content;

53          }

54         resp.setContentLength(returnStr.length());

55         resp.getOutputStream().write(returnStr.getBytes());

56     filterConfig.getServletContext().log("TextModifierFilter:After callchain.doFilter");

57      }

58      public void destroy() {

59         this.filterConfig = null;

60      }

61      public FilterConfiggetFilterConfig() {

62          returnnull;

63      }

64      public voidsetFilterConfig(FilterConfig config) {

65      }

66      public void flushBuffer()throws IOException {

67      }

68      public int getBufferSize() {

69          return 0;

70      }

71      public StringgetCharacterEncoding() {

72          returnnull;

73      }

74      public StringgetContentType() {

75          returnnull;

76      }

77      public Locale getLocale() {

78          returnnull;

79      }

80      public ServletOutputStreamgetOutputStream() throws IOException {

81          returnnull;

82      }

83      public PrintWritergetWriter() throws IOException {

84          returnnull;

85      }

86      public boolean isCommitted(){

87          returnfalse;

88      }

89      public void reset() {

90      }

91      public void resetBuffer() {

92      }

93      public void setBufferSize(intarg0) {

94      }

95      public voidsetCharacterEncoding(String arg0) {

96      }

97      public voidsetContentLength(int arg0) {

98      }

99      public voidsetContentType(String arg0) {

100     }

101     public void setLocale(Locale arg0){

102     }

103 }

【深入学习】上述代码的主要功能集中在第25行中的doFilter()方法中,在这个方法中构造了一个响应包装器。当调用chain.doFilter(request,tmrw)方法时,Servlet的响应都已经在响应包装器对象tmrw中了,之后即可调用相关的方法获取响应的内容,并进行适当的处理。

14.4.3  编写JSP和Servlet文件

前面已经开发完成了一个完整的文本过滤器的程序,这里将接着开发演示过滤器工作过程的JSP和Servlet文件。

】实现一个过滤器的程序,查看它的工作过程。首先来看利用JSP文件实现的代码。

程序textModifier.jsp

01 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值