1. 程式人生 > >深入分析JavaWeb Item39 -- 監聽器(Listener)學習進階

深入分析JavaWeb Item39 -- 監聽器(Listener)學習進階

一、監聽域物件中屬性的變更的監聽器

  域物件中屬性的變更的事件監聽器就是用來監聽 ServletContext, HttpSession, HttpServletRequest 這三個物件中的屬性變更資訊事件的監聽器。
  
  這三個監聽器介面分別是ServletContextAttributeListener, HttpSessionAttributeListenerServletRequestAttributeListener,這三個介面中都定義了三個方法來處理被監聽物件中的屬性的增加,刪除和替換的事件,同一個事件在這三個介面中對應的方法名稱完全相同,只是接受的引數型別不同。

1.1、attributeAdded 方法

  當向被監聽物件中增加一個屬性時,web容器就呼叫事件監聽器的attributeAdded方法進行響應,這個方法接收一個事件型別的引數,監聽器可以通過這個引數來獲得正在增加屬性的域物件和被儲存到域中的屬性物件
  各個域屬性監聽器中的完整語法定義為:

 public void attributeAdded(ServletContextAttributeEvent scae)
 public void attributeReplaced(HttpSessionBindingEvent  hsbe)
 public void attributeRmoved
(ServletRequestAttributeEvent srae)

1.2、attributeRemoved 方法

  當刪除被監聽物件中的一個屬性時,web容器呼叫事件監聽器的attributeRemoved方法進行響應
  各個域屬性監聽器中的完整語法定義為:

public void attributeRemoved(ServletContextAttributeEvent scae)
 public void attributeRemoved (HttpSessionBindingEvent  hsbe)
 public void attributeRemoved (ServletRequestAttributeEvent srae)

1.3、attributeReplaced 方法

  當監聽器的域物件中的某個屬性被替換時,web容器呼叫事件監聽器的attributeReplaced方法進行響應
  各個域屬性監聽器中的完整語法定義為:

public void attributeReplaced(ServletContextAttributeEvent scae)
 public void attributeReplaced (HttpSessionBindingEvent  hsbe)
 public void attributeReplaced (ServletRequestAttributeEvent srae)

1.4、ServletContextAttributeListener監聽器範例:

  編寫ServletContextAttributeListener監聽器監聽ServletContext域物件的屬性值變化情況,程式碼如下:

package me.gacl.web.listener;

import java.text.MessageFormat;

import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;

/**
* @ClassName: MyServletContextAttributeListener
* @Description: ServletContext域物件中屬性的變更的事件監聽器
* @author: 孤傲蒼狼
* @date: 2014-9-11 下午10:53:04
*
*/ 
public class MyServletContextAttributeListener implements
        ServletContextAttributeListener {

    @Override
    public void attributeAdded(ServletContextAttributeEvent scab) {
        String str =MessageFormat.format(
                "ServletContext域物件中添加了屬性:{0},屬性值是:{1}"
                ,scab.getName()
                ,scab.getValue());
        System.out.println(str);
    }

    @Override
    public void attributeRemoved(ServletContextAttributeEvent scab) {
        String str =MessageFormat.format(
                "ServletContext域物件中刪除屬性:{0},屬性值是:{1}"
                ,scab.getName()
                ,scab.getValue());
        System.out.println(str);
    }

    @Override
    public void attributeReplaced(ServletContextAttributeEvent scab) {
        String str =MessageFormat.format(
                "ServletContext域物件中替換了屬性:{0}的值"
                ,scab.getName());
        System.out.println(str);
    }
}

  在web.xml檔案中註冊監聽器

  <listener>
       <description>MyServletContextAttributeListener監聽器</description>
       <listener-class>me.gacl.web.listener.MyServletContextAttributeListener</listener-class>
   </listener>

  編寫ServletContextAttributeListenerTest.jsp測試頁面

<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
  <head>
    <title>ServletContextAttributeListener監聽器測試</title>
  </head>

  <body>
        <%
           //往application域物件中新增屬性
           application.setAttribute("name", "孤傲蒼狼");
          //替換application域物件中name屬性的值
           application.setAttribute("name", "gacl");
           //移除application域物件中name屬性
           application.removeAttribute("name");
           %>
  </body>
</html>

執行結果如下:

  這裡寫圖片描述

  從執行結果中可以看到,ServletContextListener監聽器成功監聽到了ServletContext域物件(application)中的屬性值的變化情況。

1.5、ServletRequestAttributeListener和HttpSessionAttributeListener監聽器範例:

  編寫監聽器監聽HttpSession和HttpServletRequest域物件的屬性值變化情況,程式碼如下:

package me.gacl.web.listener;

import java.text.MessageFormat;

import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

public class MyRequestAndSessionAttributeListener implements
        HttpSessionAttributeListener, ServletRequestAttributeListener {

    @Override
    public void attributeAdded(ServletRequestAttributeEvent srae) {
        String str =MessageFormat.format(
                "ServletRequest域物件中添加了屬性:{0},屬性值是:{1}"
                ,srae.getName()
                ,srae.getValue());
        System.out.println(str);
    }

    @Override
    public void attributeRemoved(ServletRequestAttributeEvent srae) {
        String str =MessageFormat.format(
                "ServletRequest域物件中刪除屬性:{0},屬性值是:{1}"
                ,srae.getName()
                ,srae.getValue());
        System.out.println(str);
    }

    @Override
    public void attributeReplaced(ServletRequestAttributeEvent srae) {
        String str =MessageFormat.format(
                "ServletRequest域物件中替換了屬性:{0}的值"
                ,srae.getName());
        System.out.println(str);
    }

    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        String str =MessageFormat.format(
                "HttpSession域物件中添加了屬性:{0},屬性值是:{1}"
                ,se.getName()
                ,se.getValue());
        System.out.println(str);
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {
        String str =MessageFormat.format(
                "HttpSession域物件中刪除屬性:{0},屬性值是:{1}"
                ,se.getName()
                ,se.getValue());
        System.out.println(str);
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) {
        String str =MessageFormat.format(
                "HttpSession域物件中替換了屬性:{0}的值"
                ,se.getName());
        System.out.println(str);
    }
}

  在web.xml檔案中註冊監聽器

  <listener>
      <description>MyRequestAndSessionAttributeListener監聽器</description>
       <listener-class>me.gacl.web.listener.MyRequestAndSessionAttributeListener</listener-class>
   </listener>

  編寫RequestAndSessionAttributeListenerTest.jsp測試頁面

<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
  <head>
    <title>RequestAndSessionAttributeListener監聽器測試</title>
  </head>

  <body>
    <%
           //往session域物件中新增屬性
           session.setAttribute("aa", "bb");
          //替換session域物件中aa屬性的值
           session.setAttribute("aa", "xx");
           //移除session域物件中aa屬性
           session.removeAttribute("aa");

           //往request域物件中新增屬性
           request.setAttribute("aa", "bb");
          //替換request域物件中aa屬性的值
           request.setAttribute("aa", "xx");
           //移除request域物件中aa屬性
           request.removeAttribute("aa");
    %>
  </body>
</html>

  執行結果如下:

  這裡寫圖片描述

  從執行結果中可以看到,HttpSessionAttributeListener監聽器和ServletRequestAttributeListener成功監聽到了HttpSession域物件和HttpServletRequest域物件的屬性值變化情況。

二、感知Session繫結的事件監聽器

  儲存在Session域中的物件可以有多種狀態:繫結(session.setAttribute(“bean”,Object))到Session中;從 Session域中解除(session.removeAttribute(“bean”))繫結;隨Session物件持久化到一個儲存裝置中;隨Session物件從一個儲存裝置中恢復
  Servlet 規範中定義了兩個特殊的監聽器介面“HttpSessionBindingListener和HttpSessionActivationListener”來幫助JavaBean 物件瞭解自己在Session域中的這些狀態: ,實現這兩個介面的類不需要 web.xml 檔案中進行註冊。

2.1、HttpSessionBindingListener介面

  實現了HttpSessionBindingListener介面的JavaBean物件可以感知自己被繫結到Session中和 Session中刪除的事件
  當物件被繫結到HttpSession物件中時,web伺服器呼叫該物件的void valueBound(HttpSessionBindingEvent event)方法
  當物件從HttpSession物件中解除繫結時,web伺服器呼叫該物件的void valueUnbound(HttpSessionBindingEvent event)方法

範例:

package me.gacl.domain;

import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

/**
* @ClassName: JavaBeanDemo1
* @Description: 
*   實現了HttpSessionBindingListener介面的 JavaBean物件可以感知自己被繫結到 Session中和從Session中刪除的事件
    當物件被繫結到 HttpSession 物件中時,web 伺服器呼叫該物件的  void valueBound(HttpSessionBindingEvent event) 方法
    當物件從 HttpSession 物件中解除繫結時,web 伺服器呼叫該物件的 void valueUnbound(HttpSessionBindingEvent event)方法

* @author: 孤傲蒼狼
* @date: 2014-9-11 下午11:14:54
*
*/ 
public class JavaBeanDemo1 implements HttpSessionBindingListener {

    private String name;

    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        System.out.println(name+"被加到session中了");
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        System.out.println(name+"被session踢出來了");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public JavaBeanDemo1(String name) {
        this.name = name;
    }
}

  上述的JavaBeanDemo1這個javabean實現了HttpSessionBindingListener介面,那麼這個JavaBean物件可以感知自己被繫結到Session中和從Session中刪除的這兩個操作,測試程式碼如下:

<%@ page language="java" pageEncoding="UTF-8"%>
<%@page import=" me.gacl.domain.JavaBeanDemo1"%>
<!DOCTYPE HTML>
<html>
  <head>
    <title></title>
  </head>

  <body>
    <% 
        //將javabean物件繫結到Session中
        session.setAttribute("bean",new JavaBeanDemo1("孤傲蒼狼"));
        //從Session中刪除javabean物件
        session.removeAttribute("bean");
    %>
  </body>
</html>

  執行結果如下:

  這裡寫圖片描述

2.2、HttpSessionActivationListener介面

  實現了HttpSessionActivationListener介面的JavaBean物件可以感知自己被活化(反序列化)和鈍化(序列化)的事件
  當繫結到HttpSession物件中的javabean物件將要隨HttpSession物件被鈍化(序列化)之前,web伺服器呼叫該javabean物件的void sessionWillPassivate(HttpSessionEvent event) 方法。這樣javabean物件就可以知道自己將要和HttpSession物件一起被序列化(鈍化)到硬碟中.
  當繫結到HttpSession物件中的javabean物件將要隨HttpSession物件被活化(反序列化)之後,web伺服器呼叫該javabean物件的void sessionDidActive(HttpSessionEvent event)方法。這樣javabean物件就可以知道自己將要和 HttpSession物件一起被反序列化(活化)回到記憶體中

範例:

package me.gacl.domain;

import java.io.Serializable;

import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;

/**
* @ClassName: JavaBeanDemo2
* @Description: 
    實現了HttpSessionActivationListener介面的 JavaBean 物件可以感知自己被活化和鈍化的事件
    活化:javabean物件和Session一起被反序列化(活化)到記憶體中。
    鈍化:javabean物件存在Session中,當伺服器把session序列化到硬碟上時,如果Session中的javabean物件實現了Serializable介面
    那麼伺服器會把session中的javabean物件一起序列化到硬碟上,javabean物件和Session一起被序列化到硬碟中的這個操作稱之為鈍化
    如果Session中的javabean物件沒有實現Serializable介面,那麼伺服器會先把Session中沒有實現Serializable介面的javabean物件移除
    然後再把Session序列化(鈍化)到硬碟中
    當繫結到 HttpSession物件中的javabean物件將要隨 HttpSession物件被鈍化之前,
    web伺服器呼叫該javabean物件物件的 void sessionWillPassivate(HttpSessionEvent event)方法
    這樣javabean物件就可以知道自己將要和 HttpSession物件一起被序列化(鈍化)到硬碟中
    當繫結到HttpSession物件中的javabean物件將要隨 HttpSession物件被活化之後,
    web伺服器呼叫該javabean物件的 void sessionDidActive(HttpSessionEvent event)方法
    這樣javabean物件就可以知道自己將要和 HttpSession物件一起被反序列化(活化)回到記憶體中
* @author: 孤傲蒼狼
* @date: 2014-9-11 下午11:22:35
*
*/ 
public class JavaBeanDemo2 implements HttpSessionActivationListener,
        Serializable {


    private static final long serialVersionUID = 7589841135210272124L;
    private String name;

    @Override
    public void sessionWillPassivate(HttpSessionEvent se) {

        System.out.println(name+"和session一起被序列化(鈍化)到硬碟了,session的id是:"+se.getSession().getId());
    }

    @Override
    public void sessionDidActivate(HttpSessionEvent se) {
        System.out.println(name+"和session一起從硬碟反序列化(活化)回到記憶體了,session的id是:"+se.getSession().getId());
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public JavaBeanDemo2(String name) {
        this.name = name;
    }
}

  為了觀察繫結到HttpSession物件中的javabean物件隨HttpSession物件一起被鈍化到硬碟上和從硬碟上重新活化回到記憶體中的的過程,我們需要藉助tomcat伺服器幫助我們完成HttpSession物件的鈍化和活化過程,具體做法如下:

  在WebRoot\META-INF資料夾下建立一個context.xml檔案,如下所示:

  這裡寫圖片描述

  context.xml檔案的內容如下:

<Context>
     <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
     <Store className="org.apache.catalina.session.FileStore" directory="gacl"/>
     </Manager>
 </Context>

  在context.xml檔案檔案中配置了1分鐘之後就將HttpSession物件鈍化到本地硬碟的一個gacl資料夾中

  jsp測試程式碼如下:

<%@ page language="java" pageEncoding="UTF-8"%>
<%@page import="me.gacl.domain.JavaBeanDemo2"%>
<!DOCTYPE HTML>
<html>
  <head>
    <title></title>
  </head>

  <body>
      一訪問JSP頁面,HttpSession就建立了,建立好的Session的Id是:${pageContext.session.id}
       <hr/>
   <% 
        session.setAttribute("bean",new JavaBeanDemo2("孤傲蒼狼"));
    %>
  </body>
</html>

  訪問這個jsp頁面,伺服器就會馬上建立一個HttpSession物件,然後將實現了HttpSessionActivationListener介面的JavaBean物件繫結到session物件中,這個jsp頁面在等待1分鐘之後沒有人再次訪問,那麼伺服器就會自動將這個HttpSession物件鈍化(序列化)到硬碟上,

  這裡寫圖片描述
  
  我們可以在tomcat伺服器的work\Catalina\localhost\JavaWeb_Listener_20140908\gacl資料夾下找到序列化到本地儲存的session,如下圖所示:

  這裡寫圖片描述

  當再次訪問這個Jsp頁面時,伺服器又會自動將已經鈍化(序列化)到硬碟上HttpSession物件重新活化(反序列化)回到記憶體中。執行結果如下:

  這裡寫圖片描述

  JavaWeb開發技術中的監聽器技術的內容就這麼多了,在平時的工作中,監聽器技術在JavaWeb專案開發中用得是比較多,因此必須掌握這門技術。