• Servlet过滤器和监听器知识总结(转)


    Servlet过滤器和监听器知识总结(转)

     Servlet过滤器是 Servlet 程序的一种特殊用法,主要用来完成一些通用的操作,如编码的过滤、判断用户的登录状态。过滤器使得Servlet开发者能够在客户端请求到达 Servlet资源之前被截获,在处理之后再发送给被请求的Servlet资源,并且还可以截获响应,修改之后再发送给用户。而Servlet监听器可以  监听客户端发出的请求、服务器端的操作,通过监听器,可以自动激发一些操作,如监听在线人数。
      Servlet过滤器简介绍
          Servlet过滤器是在Java Servlet 2.3 规范中定义的,它是一种可以插入的Web组件,它能够对Servlet 容器的接收到的客户端请求和向客户端发出的响应对象进行截获,过滤器支持对Servlet程序和JSP页面的基本请求处理功能,如日志、性能、安全、会话  处理、XSLT转换等。
      
          Servlet过滤器本身不产生请求和响应,它只提供过滤作用,Servlet过滤器能够在Servlet程序(JSP页面)被调用之前检查  request对象,修改请求头和请求内容,在Servlet程序(JSP页面)被调用之后,检查response对象,修改响应头和响应内容。

    Servlet过滤器的特点
          1.Servlet过滤器可以检查和修改request和response对象。 
      
          2.Servlet过滤器可以被指定与特定的URL关联,只有当客户请求访问该特定的URL时,才会触发过滤器。
      
          3.Servlet过滤器可以被串联成串,形成过滤链,协同修改请求和响应。
       Servlet过滤器的作用如下:
      
          1.查询请求并作出相应的行动。
          2.阻塞请求--响应对,使其不能进一步传递。
          3.修改请求头和内容,用户可以提供自定义的请求。
          4.修改响应头和内容,用户可以通过提供定制的响应版本实现。
          5.与外部资源进行交互。
      2 Servlet过滤器的体系结构
          Servlet过滤器用于拦截传入的请求和传出的响应,并监视、修改或以某种方式处理 正在通过的数据流。Servlet过滤器是自包含、模块化的组件,可以将它们添加到请求/响应过滤链中,或者在不影响应用程序中其它Web组件的情况下删 除它们。Servlet过滤器只在改动请求和响应的运行时处理,因而不应该将它们之间嵌入到Web应用程序框架,除非是通过Servlet API中良好定义的标准接口来实现。
      
          Web资源可以配置成为没有过滤器与之关联(默认情况)、与单个过滤器关联(典型情况),甚至是与一个过滤器链关联。其功能与Servlet一 样,主要是接收请求和响应对象,然后过滤器会检查请求对象,并决定是将该请求转发给链中的下一个过滤器,还是终止该请求并直接向客户端发会一个响应,如果  请求被转发了,它将被传递给过滤链中的下一个过滤器,或者Servlet程序(JSP页面),在这个请求通过过滤器链并被服务器处理后,一个响应将以相反 的顺序通过该过滤链发送回去,这样就给每个Servlet过滤器提供了根据需要处理响应对象的机会。
      
          当过滤器在Servlet 2.3规范中首次引入时,只能过滤客户端和客户端所访问的指定Web资源之间的内容(请求/响应),如果该Web资源将请求转发给其它Web资源时,那就  不能向幕后委托的任何请求应用过滤器。Servlet 2.4 规范消除了这个限制,Servlet过滤器现在可以应用于J2EE Web环境中存在请求和响应的任何地方。可见,Servlet过滤器可以应用在客户端和Servlet程序之间、Servlet程序和Servlet程序 之间、Servlet程序和JSP页面之间、JSP页面和JSP页面之间,具有强大的能力和灵活性。
      
                                                                       
      
      2.1 Servlet过滤器对请求的过滤
          Servlet过滤器对请求的过滤过程如下:
      
          1.Servlet容器创建一个Servlet过滤器实例。
          2.Servlet过滤器实例调用init()方法得到初始化参数。
          3.Servlet过滤器实例调用doFilter()方法,根据初始化参数的值判断该请求是否合法,如果该请求不合法,则阻塞该请求,如果是合法请求,则调用chain.doFilter(request,response)方法将该请求向后转发。
      
      13.2.2 Servlet过滤器对响应的过滤
          Servlet过滤器对响应的过滤过程如下:
          1.过滤器截获客户端的请求。
          2.重新封装ServletResponse,在封装后的ServletResponse中提供客户端自定义的输出流。
          3.将请求向后转发。
          4.Web组件产生响应。
          5.过滤器从被封装的ServletResponse中获取客户自定义的输出流。
          6.将响应内容通过客户自定义的输出流写入缓冲流。
          7.在缓冲流中修改响应内容后清空缓冲流,输出响应内容。
      
      2.3 Servlet过滤器的发布 
          Seevlet过滤器设计完毕之后,必须对该过滤器进行发布(配置), 发布一个Servlet过滤器时,必须在项目的web.xml文件中加入<filter>元素和<filter- mapping>元素,<filter>元素用来定义一个过滤器,该元素的属性有:

    属性

    描述

    filter-name

    指定过滤器的名字

    filter-class

    指定过滤器类

    init-param

    指定过滤器的初始化参数

        <filter-mapping>元素用于将过滤器与URL关联,其属性有:

    属性

    描述

    filter-name

    指定过滤器的名字

    url-pattern

    指定与过滤器关联的URL

    3 实现一个Servlet过滤器
      3.1 Servlet过滤器接口的构成
          所有的Servlet过滤器都必须实现javax.servlet.filter接口,该接口中定义了3个过滤器必须实现的方法:
      
          1.
    void  init(FilterConfig):过滤器的初始化方法,Servlet容器在创建过滤器实例时调用这个方法,在这个方法中可以读出在web.xml文件中为该过滤器配置的初始化参数。
      
          2.
    voiddoFilter(ServletRequest,ServletResponse,FilterChain):用于完成实际的过滤操作,当客户请求访问与过滤器相关联的URL时,Servlet容器将先调用过滤器的这个方法,FilterChain参数用于访问后续过滤器。
      
          3.
    voiddestroy():过滤器在被取消前执行这个方法,释放过滤器申请的资源。
      
      3.2 Servlet过滤器的创建步骤
          创建一个Servlet过滤器需要下面的步骤:
          
          1.创建一个实现了javax.servlet.Filter接口的类。
      
          2.重写init(FilterConfig)方法,读入为过滤器配置的初始化参数,申请过滤器需要的资源。
      
          3.重写方法doFilter(ServletRequest,ServletResponse,FilterChain),完成过滤操作,可以  从ServletRequest参数中得到全部的请求信息,从ServletResponse参数中得到全部的响应信息。
      
          4.在doFilter()方法的最后,使用FilterChain参数的doFilter()方法将请求和响应后传。
      
          5.对响应的Servlet程序和JSP页面注册过滤器,在部署描述文件(web.xml)中使用<filter-apping>和<filter>元素对过滤器进行配置。
      
      3.3 编写过滤器类
          在过滤器中,需要使用3个简单的接口,它们是:分别是Filter、FilterChain、FilterConfig,全部包含在
    javax.servlet包中。从编程的角度看,过滤器类要实现Filter接口,然后使用实现了FilterChain和FilterConfig接口的对象来工作,FilterChain对象负责将请求和响应后传,FilterConfig对象负责为过滤器读初始化参数。
      
          为了与过滤器的三步模式(创建、工作、撤消)保持一致,过滤器必须重写Filter接口中的三个方法:
      
          
    init():在容器实例化过滤器市时被调用,主要为过滤器做初始化,该方法有一个FilterConfig类型的形参。
      
          
    doFilter():这个方法用来完成真正的过滤操作,它有3个形式参数:ServletRequest参数包含请求信息,ServletResponse参数包含响应信息,FilterChain参数用来将请求和响应向后传递。
      
          
    destroy():过滤器被撤消时调用这个方法,释放过滤器所 占有的资源。
      
          在下面的例子中实现了一个简单的Servlet过滤器(SessionFilter.java),它实现的功能是判断客户是否成功登录,如果成功登录,转向正确页面,否则返回一个错误页面,提示客户应该进行登录。该过滤器代码如下:
      
          //includeList:数组,受保护的资源。
          //logonList:数组,登录页面。
          package ch13;
          import javax.servlet.*;
          import javax.servlet.http.*;
          import java.io.*;
          public class SessionFilter implements 
    Filter{
              String  logonStrings,includeStrings,redirectPath,disabletestfilter;
              String[]  logonList,includeList;
      
              private boolean  isContains(String containers,String[] regx) {
                  boolean  result=false;
                  for(int  i=0;i<regx.length;i++) {
                      if  (containers.indexOf(regx[i])!=-1)
                          return  true;
                  }
                  return  result;
              }
      
              public FilterConfig config;
              private void  setFilterConfig(FilterConfig config) {
                  this.config=config;
              }
      
              private FilterConfig  getFilterConfig(){
                  return  config;
              }
      
              //必须重写
              public void init(
    FilterConfig filterConfig) throws ServletException{
                  this.config=filterConfig;
                  logonStrings=config.getInitParameter("logonStrings");
                   includeStrings=config.getInitParameter("includeStrings");
                   redirectPath=config.getInitParameter("redirectPath");
                   disabletestfilter=config.getInitParameter("disabletestfilter");
                  logonList=logonStrings.split(";");//分割为数组
                   includeList=includeStrings.split(";");//分割为数组
              }
              //必须重写
              public void  doFilter(ServletRequest request,ServletResponse response,
    FilterChain 
                                          chain)  throws ServletException, IOException {
                  HttpServletRequest  httpreq=(HttpServletRequest)request;
                  HttpServletResponse  httpres=(HttpServletResponse)response;
                  HttpServletResponseWrapper  wrapper=new HttpServletResponseWrapper(
      (HttpServletResponse)response);
                  if  (disabletestfilter.toUpperCase().equals("Y")){
                      chain.doFilter(request,response);//如果不过滤
                      return;
                  }
                  Object  user=httpreq.getSession().getAttribute("
    userinfo");
                  if  (user==null){//该用户没有登录
                      if  (!isContains(httpreq.getRequestURI(),includeList)){
                          chain.doFilter(request,response);
                          return;//访问的是不受保护的页面,可以
                      }
                      if  (isContains(httpreq.getRequestURI(),logonList)){
                          chain.doFilter(request,response);
                          return;  //访问的是登录页面,可以
                      }
                      wrapper.sendRedirect(redirectPath);  //转向登页面 
                  }else  {//该用户已经登录
                      chain.doFilter(request,response);
                  }
              }
              //必须重写
              public void destroy() {
                  config=null;
              }
          }
      
          在上面的这个Servlet过滤器程序中,根据用户session对象中有无
    userinfo这个属性来确定该用户是否已经登录。
      
      3.4 配置部署过滤器
          在WEB-INF/web.xml文件中用以下代码配置过滤器:
      
          <filter>
              <filter-name>SessionFilter</filter-name>
                  <filter-class>ch13.SessionFilter</filter-class>  
              <init-param>
                  <param-name>logonStrings</param-name>
                  <param-value>Login.jsp</param-value>  
              </init-param> 
              <init-param>
                  <param-name>includeStrings</param-name>
                  <param-value>.jsp
    ;.html;.htm</param-value> 
              </init-param> 
              <init-param>
                  <param-name>redirectPath</param-name>
                  <param-value>./Login.jsp</param-value>  
              </init-param> 
              <init-param>
                  <param-name>disabletestfilter</param-name>
                  <param-value>n</param-value>  
              </init-param> 
          </filter> 
          <filter-mapping>
              <filter-name>SessionFilter</filter-name>
              <url-pattern>/*</url-pattern>
          </filter-mapping>
      
          在上面的配置中,参数logonStrings指定可以访问的登录页面,参数includeStrings指定受保护的资源的后缀,参数redirectPath表示没有登录时转向的登录页面,参数disabletestfilter表示过滤器是否有效。而 /* 表示过滤器与所有的URL都关联(对所有的访问请求都进行过滤)。在浏览器中访问任意的资源时,都要通过这个过滤器的过滤。
      
      4 过滤器的应用案例.4.1 版权过滤器的应用案例
          在一个Web应用中的所有页面的下面添加上版权信息,通常的做法是采用<%@ include>指令或<c:import> 标签,使用过滤器也是一个好办法。
      
          
    1.编写过滤器类CopyrightFilter.java
          package ch13;
          import javax.servlet.*;
          import javax.servlet.http.*;
          import java.io.*;
          public class CopyrightFilter implements 
    Filter{
              private String date;
              public FilterConfig config;
              //必须重写
              public void init(
    FilterConfig filterConfig) throws ServletException{
                  this.config=filterConfig;
                  date=config.getInitParameter("date");
              }
              //必须重写
              public void 
    doFilter(ServletRequest request,ServletResponse response,FilterChain 
                                                  chain)  throws ServletException, IOException {
                  chain.doFilter(request,response);
                  PrintWriter  out=response.getWriter();
                  out.print("<br><center><font  size='3' color='red'>版权所有:北京工业大学
                                                                  </center></font>");
                  if  (date!=null) 
                      out.print("<br><center><font  color='blue'>"+date+"</center></font>");
                  out.flush();
              }
              //必须重写
              public void 
    destroy() {
                  config=null;
              }
          }
      
          在这个过滤器中,在doFilter()方法的最后,通过response对象得到一个输出流out,然后通过输出流向客户端输出版权信息,这样,每个页面的最后都会出现过滤器添加的版权信息。
      
          
    2.修改web.xml,配置该过滤器
          <filter>
              <filter-name>CopyrightFilter</filter-name>
                  <filter-class>ch13.CopyrightFilter</filter-class>  
              <init-param>
                  <param-name>date</param-name>
                  <param-value>2010-9</param-value>  
              </init-param> 
          </filter> 
          <filter-mapping>
              <filter-name>CopyrightFilter</filter-name>
              <url-pattern>/*</url-pattern>
          </filter-mapping>
      
          
    3.测试
          在浏览器中任意访问一个页面,都可以在看到在页面的下部出现过滤器添加的版权信息。
      
      4.2 禁止未授权的IP访问站点过滤器的应用案例
          使用过滤器禁止未授权的IP访问站点是过滤器常见的应用,本例演示了如何利用过滤器实现禁止未授权的IP访问站点。
      
          
    1.编写过滤器类FilterIP.java
          package ch13;
          import javax.servlet.*;
          import javax.servlet.http.*;
          import java.io.*;
          public class FilterIP implements 
    Filter{
              private String  filterIP,error;
              public FilterConfig config;
              //必须重写
              public void 
    init(FilterConfig filterConfig) throws  ServletException{
                  this.config=filterConfig;
                  filterIP=config.getInitParameter("FilterIP");
                  if  (filterIP==null) filterIP="";
                  error=config.getInitParameter("ERROR");
                  if  (error==null) error="error.jsp";
              }
              //必须重写
              public void 
    doFilter(ServletRequest request,ServletResponse response,FilterChain 
                                                  chain)  throws ServletException, IOException {
                  RequestDispatcher  dispatcher=request.getRequestDispatcher("ErrorInfo.jsp");
                  
    String remoteIP=request.getRemoteAddr();//得到客户的IP地址
                  if  (remoteIP.equals(filterIP)) {
                      dispatcher.forward(request,response);
                      return;
                  }  else
                      chain.doFilter(request,response);
              }
              //必须重写
              public void 
    destroy() {
                  config=null;
              }
          }
      
          在这个过滤器中,在doFilter()方法内,通过request对象得到客户端的IP地址,如果客户端的IP是被禁止的IP,则使用request对象将请求转发给一个出错页面。
      
          
    2.修改web.xml,配置过滤器
      <filter>
              <filter-name>FilterIP</filter-name>
                  <filter-class>ch13.FilterIP</filter-class>  
              <init-param>
                  <param-name>FilterIP</param-name>
                  <param-value>192.168.1.1</param-value>  
              </init-param> 
              <init-param>
                  <param-name>ERROR</param-name>
                  <param-value>error.jsp</param-value>  
              </init-param>
          </filter> 
          <filter-mapping>
              <filter-name>FilterIP</filter-name>
              <url-pattern>/*</url-pattern>
          </filter-mapping>
      
          对来自192.168.1.1的客户的所有请求(/*)都进行过滤,转移到error.jsp页面。
      
          
    3.编写出错页面error.jsp
          <%@  page contentType="text/html;charset=gb2312" %>
          网站不允许IP地址为192.168.1.1的计算机访问。
      
          在IP地址为  192.168.1.1 的计算机上访问网站的任何一个资源,都会转移到error.jsp页面。

      
      4.3 过滤页面内容(响应内容)
          本过滤器使用HttpServletResponseWrapper类  来实现页面内容的过滤,它的原理是让Web资源先将页面内容(响应内容)写入到HttpServletResponseWrapper对象中,然后再在过  滤器中处理HttpServletResponseWrapper对象中的页面内容(响应内容),最后再将处理好的页面内容(响应内容)发送给客户。
      
          
    1.编写HttpServletResponseWrapper类的子类.java
          package ch13;
          import java.io.ByteArrayOutputStream; 
          import java.io.OutputStreamWriter; 
          import java.io.IOException; 
          import java.io.PrintWriter; 
          import java.io.UnsupportedEncodingException; 
          import javax.servlet.http.HttpServletResponse; 
          import javax.servlet.http.HttpServletResponseWrapper;
          import javax.servlet.ServletOutputStream;
          public class 
    WrapperResponse extends  HttpServletResponseWrapper { 
              public static final int  OT_NONE = 0, OT_WRITER = 1, OT_STREAM = 2; 
              private int outputType =  OT_NONE; 
              private ServletOutputStream  output = null; 
              private PrintWriter writer =  null; 
              private ByteArrayOutputStream  buffer = null; 
      
              //构造函数 
              public  WrapperResponse(HttpServletResponse resp) throws IOException { 
                  super(resp);  
                  buffer  = new ByteArrayOutputStream(); 
              } 
      
              //得到字符输出流
              public PrintWriter 
    getWriter() throws IOException { 
                  if  (outputType == OT_STREAM) 
                      throw  new IllegalStateException(); //已经用了OutputStream流
                  else  if (outputType == OT_WRITER) 
                      return  writer; 
                  else  { 
                      outputType  = OT_WRITER; 
                      writer  = new PrintWriter(new  OutputStreamWriter(buffer, getCharacterEncoding())); 
                      return  writer; 
                  } 
              }
      
              //得到字节输出流
              public ServletOutputStream 
    getOutputStream() throws IOException { 
                  if  (outputType == OT_WRITER) 
                      throw  new IllegalStateException(); //已经用了Writer流
                  else  if (outputType == OT_STREAM) 
                      return  output; 
                  else  { 
                      outputType  = OT_STREAM; 
                      output  = new 
    WrappedOutputStream(buffer); 
                      return  output; 
                  } 
              } 
      
              //刷新输出内容
              public void 
    flushBuffer() throws IOException { 
                  if  (outputType == OT_WRITER) 
                      writer.flush();  
                  if  (outputType == OT_STREAM) 
                      output.flush();  
              } 
      
              //输出缓冲区复位
              public void 
    reset() { 
                  outputType  = OT_NONE; 
                  buffer.reset();  
              } 
              public String  getResponseData() throws IOException { 
                  flushBuffer();  
                  return  new String(buffer.toByteArray()); 
              } 
      
              //内部类,将数据写入自己的定义的缓冲区
              class 
    WrappedOutputStream extends ServletOutputStream { 
                      private  ByteArrayOutputStream buffer; 
                      public  WrappedOutputStream(ByteArrayOutputStream buffer) { 
                          this.buffer  = buffer; 
                      }  
                      public  void write(int b) throws IOException { 
                          buffer.write(b);  
                      }  
                      public  byte[] toByteArray() { 
                          return  buffer.toByteArray(); 
                      }  
              } 
          } 
      
          在这个类中,一定要重写response对象的关于输出流(outputStream、writer)操作的方法:getOutputStream()、getWriter()、flushBuffer()、reset()。
      
          
    2.编写过滤器GavinFilter.java
          package ch13;
          import java.io.IOException; 
          import javax.servlet.*; 
          import javax.servlet.http.HttpServletRequest; 
          import javax.servlet.http.HttpServletResponse; 
          public class 
    GavinFilter implements Filter { 
              private String  oldword="%" , newword="百分号"; 
              public void destroy(){}
              public void  doFilter(ServletRequest request, ServletResponse response, 
    FilterChain
                                                      chain)  throws IOException, ServletException { 
                  HttpServletResponse  oldresponse = (HttpServletResponse)response; 
                  WrapperResponse  
    wrapperResponse = new WrapperResponse(oldresponse); 
                  chain.doFilter(request,  
    wrapperResponse); //让服务器将响应内容写到Wrapper中
                  String  html = wrapperResponse.getResponseData(); //取出响应内容
                  oldresponse.getWriter().print(html.replaceAll(oldword,  newword)); //替换页面中的文字,然后发送给客户
              } 
              public void init(
    FilterConfig config) throws ServletException {
                  oldword=config.getInitParameter("oldword");
                  newword=config.getInitParameter("newword");
              } 
          }
      
          该过滤器将页面内容(响应内容)中的字符 
    % 替换为百分号三个汉字,由此可见,实现了对响应内容的过滤。 
      
          
    3.对该过滤器的配置
          <filter> 
              <filter-name>gavinFilter</filter-name>  
              <filter-class>ch13.GavinFilter</filter-class>  
              <init-param> 
                  <param-name>oldword</param-name>  
                  <param-value>
    %</param-value> 
              </init-param> 
              <init-param> 
                  <param-name>newword</param-name>  
                  <param-value>
    百分号</param-value> 
              </init-param> 
          </filter>
          <filter-mapping> 
              <filter-name>gavinFilter</filter-name>  
              <url-pattern>/*</url-pattern>  
          </filter-mapping> 
      
      5 Servlet监听器
          Servlet监听器也叫做 listener,通过它可以监听Web应用的上下文(环境)信息、Servlet请求信息、Servlet会话信息,并自动根据不同情况,在后台调用相  应的处理程序。通过监听器,可以自动激发一些操作,比如监听在线人数,当增加一个HttpSession时就激发 sessionCreated(HttpSessionEvent)方法,这样就可以给在线人数加1。
      
      13.5.1 监听器的原理
          Servlet监听器是Web应用开发的一个重要组成部  分,Servlet监听器是在Servlet2.3规范中和Servlet过滤器一起引入的。在 Servlet2.4 规范中对其进行了比较大的改进。主要就是用来对Web应用进行监督和控制,极大地增强了Web应用的事件处理能力。
      
          Servlet监听器的功能比较类似于Java中的GUI程序的监听器,可以监听由于Web应用中的状态改变而引起的Servlet容器产生的相应事件,然后接收并处理这些事件。
      
      5.2 监听器的类型
          在 Servlet 2.4 规范中,根据监听对象的类型和范围,将监听器分为3类:
    ServletRequest监听器(请求监听器)、HttpSession监听器(会话监听器)、ServletContext监听器(上下文监听器),其中请求监听器(ServletRequest监听器)是  Servlet 2.4 规范中新增加的监听器,可以用来监听客户的端请求,在Servlet 2.4 规范中包含了8个监听器接口和6个监听器事件类,具体的监听器接口和事件如下表:

    监听对象

    监听接口

    监听事件

    ServletRequest

    ServletRequestListener    (2个方法)

    ServletRequestEvent

    ServletRequestAttributeListener    (3个方法)

    ServletRequestAttributeEvent

    HttpSession

    HttpSessionListener    (2个方法)

    HttpSessionEvent

    HttpSessionActivationListener    (2个方法)

    HttpSessionAttributeListener    (3个方法)

    HttpSessionBindingEvent

    HttpSessionBindingListener    (2个方法)

    ServletContext

    ServletContextListener    (2个方法)

    ServletContextEvent

    ServletContextAttributeListener    (3个方法)

    ServletContextAttributeEvent

        1.被监听对象ServletContext
          对ServletContext对象(JSP页面中称为application对象)实现监听涉及2个接口:
      
          (1)
    ServletContextListener接口:用于监听ServletContext对象的创建和删除:接口中定义的回调方法有:
      
              当创建一个ServletContext对象时,激发  
    contextInitialzed(ServletContextEvent)方法。
              当撤消一个ServletContext对象时,激发  
    contextDestroyed(ServletContextEvent)方法。
      
          (2)
    ServletContextAttributeListener接口:用于监听ServletContext对象的属性操作。接口中定义的回调方法有:
      
              增加属性时,激发 
    attributeAdded(ServletContextAttributeEvent)
              删除属性时,激发 attributeRemoved(ServletContextAttributeEvent) 
              修改属性时,激发 
    attributeReplaced(ServletContextAttributeEvent) 
      
          
    2.被监听对象HttpSession
          对HttpSession对象(session)实现监听涉及4个接口:
      
          (1)
    HttpSessionListener接口:这个接口监听Http会话的创建和撤消,并在某个session对象建立和销毁之前调用某个方法。接口中定义的回调方法有:
      
              创建一个session对象时,激发  
    sessionCreated(HttpSessionEvent)
              删除一个session对象时,激发 sessionDestroyed(HttpSessionEvent)
      
          (2)
    HttpSessionActivationListener接口:监听Http会话的active和passivate状态。接口中定义的回调方法有:
      
              session对象被保存到磁盘时,激发 
    sessionWillPassivate(HttpSessionEvent)
              session对象被调入内存时,激发 sessionDidActivate(HttpSessionEvent)
      
          Activate与Passivate是用于置换session对象的动作,当Web服务器因为资源利用或负载平衡等原因要将内存中的 session对象暂时储存至硬盘或其它储存器时(通过对象序列化),所作的动作称之为Passivate,而硬盘或储存器上的session对象重新加 载到JVM中时所采的动作称之为Activate。sessionDidActivate()方法与 sessionWillPassivate()方法分别于Activeate后与Passivate前被调用。 
      
          (3)
    HttpSessionAttributeListener接口:监听Http会话中属性的设置信息。接口中定义的回调方法有:
      
              向某个session对象中增加新属性时,激发  
    attributeAdded(HttpSessionBindingEvent)
              删除某个session对象中的属性时,激发 attributeRemoved(HttpSessionBindingEvent)
              修改某个session对象中的属性时,激发 attributeReplaced(HttpSessionBindingEvent)
      
          使用HttpSessionBindingEvent事件类对象的getSession()方法可以得到这个session对象,使用 HttpSessionBindingEvent对象的getName()方法得到属性的名字,使用getValue()方法得到属性的值。
      
          若有属性加入到某个会话(HttpSession)对象,则会调用attributeAdded(),同理在替换属性与移除属性时,会分别调用attributeReplaced()、attributeRemoved()。

        (4)HttpSessionBindingListener接口:这是唯一一个不需要在web.xml中进行配置的监听器接口,监听Http会话中属性的变化情况。接口中定义的回调方法有:
      
              属性被加入到session中时,激发属性的 
    valueBound(HttpSessionBindingEvent)
              属性被从session中删除时,激发属性的 valueUnbound(HttpSessionBindingEvent)
      
          使用HttpSessionBindingEvent事件类对象的getSession()方法可以得到这个session对象,使用 HttpSessionBindingEvent对象的getName()方法得到属性的名字,使用getValue()方法得到属性的值。
      
          如果一个对象object实现了HttpSessionBindingListener接口时,当把object对象保存到session中时, 就会自动调用object对象的valueBound()方法,如果对象object被从session(HttpSession)移除时,则会调用 object对象的valueUnbound()方法。使用这个接口,可以让一个对象自己知道它自己是被保存到了session中,还是从session 中被删除了。     
          
          
    3.被监听对象ServletRequest
          对ServletRequest对象(request)实现监听涉及2个接口:
      
          (1)
    ServletRequestListener接口:监听请求的创建和撤消,该接口用来监听请求到达和结束,因此可以在请求达到前和请求结束前执行一些用户行为。  接口中定义的回调方法有: 
      
          请求对象初始化时,激发 
    requestInitialized(ServletRequestEvent)
          请求对象被撤消时,激发  requestDestroyed(ServletRequestEvent)
      
          在request(HttpServletRequest)对象建立或被消灭时,会分别调用requestInitialized()和requestDestroyed()方法。
      
          (2)
    ServletRequestAttributeListener接口:监听请求中(request对象中)的属性变化。接口中定义的回调方法有:
      
          向某个request对象中增加属性时被调用attributeAdded(ServletRequestAttributeEvent)方法。
          从某个request对象中删除属性时被调用attributeRemoved(ServletRequestAttributeEvent)方法。
          修改某个request中的属性时被调用attributeReplaced(ServletRequestAttributeEvent)方法。
      
          使用ServletRequestEvent类的getServletRequest()方法可以得到这个被监听的请求对象,使用  ServletRequestAttributeEvent类的getName()方法可以得到属性名,getValue()方法可以得到属性的值。
      
          若有属性加入到某个request对象中时则会调用attributeAdded(),同理在替换属性与删除属性时,会分别调用attributeReplaced()、 attributeRemoved()。 
      
          当Web应用程序启动后,在处理任何请求之前,调用contextInitialzed()方法和getInitParamter()方法,返回  在配置文件中为定义的环境初始化信息。不同的组件,如Servlet、JSP、监听器和过滤器等,通过ServletRequest、 HttpSession 和 ServletContext达到数据共享,这些类都提供了下面的一组方法,可以使用这组方法来设置、获取、删除属性:
      
              public void  setAttribute("属性名",属性值);
              public Object  getAttribute("属性名");
              public void  removeAttribute("属性名");
           
      5.3 监听器管理共享数据库连接
          在web.xml中,使用<listener>来配置监听器,语法是:
      
              <listener>
                  <listener-class>包名.类名</listener-class>
              </listener>
      
          比如:创建一个ServletContext对象监听器,在一个Web项目一启动就创建一个与数据库的连接,保存在application对象中,这个连接一直保存到Web项目关闭时为止。程序代码如下:
      
          package ch13;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import java.sql.*;
          //import ch7.db.*; 
          public final class MyConnectionManager implements 
    ServletContextListener {
              Connection con=null;
              public void  contextInitialized(ServletContextEvent e) {//重写接口定义的方法,项目启动是调用该方法
                  
    ConnectDB db=new ConnectDB();
                  con=db.getConnection();  //使用对象db创建数据库连接
                  e.getServletContext().setAttribute("con",con);//与数据库的连接保存入application对象中
              }
              public void contextDestroyed(ServletContextEvent  e) {//重写接口定义的方法,项目关闭时调用该方法
                  try {
                      con.close();
                  }
                  catch(Exception  e1){}
              }
          }
      
          在web.xml文件对这个ServletContext类型的监听器进行配置:
      
              <listener>
                  <listener-class>
    ch13.MyConnectionManager</listener-class>
              </listener> 
      
          这个监听器能保证每新创建一个ServletContext对象时(一个Web项目只有一个 ServletContext对象),该Web项目都会有一个可以使用的数据库连接,并且这个数据库连接会在该ServletContext对象关闭(结 束)的时候随之关闭。
      
          测试页面testcon.jsp:
          <%@ page contentType="text/html" pageEncoding="GB18030"%>
          <br><%= "得到的数据库连接:"+application.getAttribute("con") %>
          <br><h1>请注意安装数据库的驱动程序</h1>
      
      
      5.4 监听器的应用案例
          下面是一个在线用户数量监听器,这个监听器可以实时统计在线人数,在 ServletContext初始化和撤消时,在服务器控制台打印出对应信息,当ServletContext对象里的属性增加、修改、删除时,在服务器  控制台打印相应的信息。要完成上面的监听功能,需要使用3个接口:
      
          HttpSessionListener:监督HttpSession对象的创建和撤消,统计人数。
          ServletContextListener:监督ServletContext对象的创建和撤消。
          ServletContextAttributeListener:监督ServletContext的属性变化。
      
          
    1.监听器程序代码OnLineCountListener.java
          package ch13;
          import javax.servlet.*;
          import javax.servlet.http.*;
          public final class 
    OnLineCountListener implements  HttpSessionListener,
                                      ServletContextAttributeListener,  ServletContextListener {
              private int count;
              private ServletContext  context=null;
      
              //构造函数 
              public OnLineCountListener()  {
                  count=0;//人数
              }
      
              //重写HttpSessionListener接口中的2个方法,完成对session对象创建和撤消的监视
              public void  sessionCreated(HttpSessionEvent se) {//创建了一个session对象
                  count++;//人数加1
                  setContext(se);
              }
              public void  sessionDestroyed(HttpSessionEvent se){//撤消了一个session对象
                  count--;//人数减1
                  setContext(se);
              }
      
              private void  setContext(HttpSessionEvent se){
                  se.getSession().getServletContext().setAttribute("onLine",new  Integer(count));
              }
      
              //重写ServletContextAttributeListener接口中的3个方法
              public void  attributeAdded(ServletContextAttributeEvent event) {//添加了属性
                  log("attributeAdded("+event.getName()+","+event.getValue()+")");
              }
              public void  attributeRemoved(ServletContextAttributeEvent event) {//删除了属性
                  log("attributeRemove("+event.getName()+","+event.getValue()+")");
              }
              public void attributeReplaced(ServletContextAttributeEvent  event) {//替换了原有的属性
                  log("attributeReplaced("+event.getName()+","+event.getValue()+")");
              }
      
              //重写ServletContextListener接口中的2个方法
              public void  contextDestroyed(ServletContextEvent event) {//Web项目关闭
                  log("contextDestroyed()");
                  context=null;
              }
              public void  contextInitialized(ServletContextEvent event) {//Web项目启动
                  this.context=event.getServletContext();
                  log("contextInitialized()");
              }
      
              //显示信息
              private void log(String  message){
                  System.out.println("ContextListener:"+message);
              }
          }
      
          在OnLineCountListener类中,用count保存目前在线人数,每增加一个session对象,人数加1,每撤消一个session对象,人数减1。人数保存在ServletContext对象中,使得任何页面都可以使用。
      
          
    2.在web.xml文件中配置监听器
      
              <listener>
                  <listener-class>
    ch13.OnLineCountListener</listener-class>
              </listener> 
      
          
    3.编写测试页面(2个)
                                  listener.jsp------>exit.jsp
      
                                  listener.jsp页面内容 
          <%@ page  contentType="text/html;charset=gb2312" %>
          目前在线人数:<font  color="red"><%=application.getAttribute("onLine")%></font><br>
          退出会话:
          <form action="exit.jsp"  method="post">
          <input type="submit"  value="exit">
          </form> 
      
                                  exit.jsp页面内容
          <%@ page  contentType="text/html;charset=gb2312" %>
          你已经退出会话<%  session.invalidate(); %> 
      
          可以单独启动5个浏览器窗口,每个窗口代表一个客户,因此在线人数是5。 
      
      5.5 HttpSessionBindingListener 接口的使用
          设计一个学生对象Student,当将该学生对象存入 session中时,他的年龄增加10岁,当将这个学生对象从session中删除时,他的年龄减少5岁。
      
          学生类Student.java
          package ch13;
          import javax.servlet.*;
          import javax.servlet.http.*;
          public class 
    Student implements  HttpSessionBindingListener {
              private int age=30;
              public void valueBound(HttpSessionBindingEvent  arg0) {//存入session时自动调用
                  age+=10;
              }
              public void  valueUnbound(HttpSessionBindingEvent arg0) {//从session中删除时自动调用
                  age-=5;
              }
              public int getAge() {return  age;}
          } 
      
          测试页面bind.jsp
          <%@ page  contentType="text/html;charset=gb2312"  import="ch13.Student"%>
          <%
              Student student=new  Student();
              out.println("学生年龄:"+student.getAge()+"<br>"); 
              session.setAttribute("st",student);
              out.println("存入session后,该学生年龄:"+student.getAge()+"<br>");
              session.removeAttribute("st");  
              out.println("从session删除,该学生年龄:"+student.getAge()+"<br>");  
          %> 

  • 相关阅读:
    [转] 数学原理浅谈一下《黑客帝国》
    [转] 应聘Java笔试时可能出现问题及其答案(第八部分)
    经典排序之堆排序
    求全排列算法简单的实现
    求一个整数因式分解
    adk环境变量配置
    经典排序之希尔排序
    快速求幂算法
    判断一个数字是否为素数的基于C语言的算法
    高次幂求模
  • 原文地址:https://www.cnblogs.com/bchjazh/p/6653367.html
Copyright © 2020-2023  润新知