jsp 監聽器


  Servlet API提供了一系列的事件和事件監聽接口。 上層的servlet/JSP應用能夠通過調用這些API進行事件 驅動的開發。這里監聽的所有事件都繼承自 java.util.Event對象。監聽器接口可以分為三類: ServletContext、HttpSession 和ServletRequest 。

一.監聽器接口和注冊

1. 監聽器接口主要在 javax.servlet 和javax.servlet.http 的包中。有以下這些接口:

  • javax.servlet.ServletContextListener:它能夠響應 ServletContext生命周期事件,它提供了 ServletContext創建之后和ServletContext關閉之前的 會被調用的方法。
  • javax.servlet.ServletContextAttributeListener:它能夠 響應ServletContext范圍的屬性添加、刪除、替換事 件。
  • javax.servlet.http.HttpSessionListener:它能夠響應 HttpSession的創建、超時和失效事件
  • javax.servlet.http.HttpSessionAttributeListener:它能 響應HttpSession范圍的屬性添加、刪除、替換事 件。
  • javax.servlet.http.HttpSessionActivationListener:它在 一個HttpSession激活或者失效時被調用。
  • javax.servlet.http.HttpSessionBindingListener:可以 實現這個接口來保存HttpSession范圍的屬性。當有 屬性從HttpSession添加或刪除時,
  • HttpSessionBindingListener 接口能夠做出響應。
  • javax.servlet.ServletRequestListener:它能夠響應一 個ServletRequest的創建或刪除。
  • javax.servlet.ServletRequestAttributeListener:它能響 應ServletRequest范圍的屬性值添加、刪除、修改事 件。
  • javax.servlet.AsyncListener:一個用於異步操作的監 聽器,在第11章會進行更詳細的介紹。

2. 編寫一個監聽器,只需要寫一個Java類來實現對應 的監聽器接口就可以了。在Servlet 3.0和Servlet 3.1中提 供了兩種注冊監聽器的方法。

第一種是使用 WebListener注解。例如:

WebListener
public class ListenerClass implements ListenerInterface {
}

第二種方法是在部署描述文檔中增加一個listener元 素。

</listener>
<listener-class>fully-qualified listener class</listener-cl
ass>
</listener>

   你可以在一個應用中添加多個監聽器,這些監聽器 是同步工作的。

二.Servlet Context 監聽器

  ServletContext的監聽器接口有兩個: ServletContextListener和 ServletContextAttributeListener。

1. ServletContextListener

  ServletContextListener能對ServletContext的創建和 銷毀做出響應。當ServletContext初始化時,容器會調用 所有注冊的ServletContextListeners的contextInitialized 方 法。該方法如下:

void contextInitialized(ServletContextEvent event)

  當ServletContext將要銷毀時,容器會調用所有注冊 的ServletContextListeners的context Destroyed 方法。該 方法如下:

void contextDestroyed(ServletContextEvent event)

  contextInitialized 方法和contextDestroyed方法都會 從容器獲取到一個 ServletContextEvent。 javax.servlet.ServletContextEvent是一個 java.util.EventObject的子類 ,它定義了一個訪問 ServletContext的getServletContext 方法,通過這個方法能夠輕松地獲取到ServletContext。:

ServletContext getServletContext()

例:  AppListener類實現了ServletContextListener 接口,它在 ServletContext剛創建時,將一個保存國家編碼和國家名 的Map放置到ServletContext中。

AppListener類

package listener;

import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class AppListener implements ServletContextListener{
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        
    }
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext servletContext = sce.getServletContext();
        
        Map<String,String> countries = new HashMap<String,String>()    ;
        countries.put("ca", "Cannada");
        countries.put("us","United States");
        servletContext.setAttribute("countries",countries);
    }
}

  注意,contextInitialized 方法。它 通過調用getServletContext方法從容器獲得了 ServletContext,然后創建了一個Map用於保存國家編碼 和國家名,再將這個Map放置到ServletContext里。在實 際開發中,往往是把數據庫里的數據放置到 ServletContext里。

countries.jsp用到了這個監聽器。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c"  uri="http://java.sun.com/jsp/jstl/core"  %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Country list</title>
</head>
<body>
we operate in these countries:
<ul>
    <c:forEach items="${countries}"  var="country">
        <li>${country.value}</li>
    </c:forEach>
</ul>
</body>
</html>

結果(resulte)

2.ServletContextAttributeListener

  當一個ServletContext范圍的屬性被添加、刪除或者 替換時,ServletContextAttributeListener接口的實現類會 接收到消息。這個接口定義了如下三個方法:

void attributeAdded(ServletContextAttributeEvent event)
void attributeRemoved(ServletContextAttributeEvent event)
void attributeReplaced(ServletContextAttributeEvent event)

  attributeAdded方法在一個ServletContext范圍屬性被 添加時被容器調用。attributeRemoved方法在一個 ServletContext范圍屬性被刪除時被容器調用。而 attributeReplaced方法在一個ServletContext范圍屬性被 新的替換時被容器調用。

  這三個方法都能獲取到一個 ServletContextAttributeEvent的對象,通過這個對象可以 獲取屬性的名稱和值。

  ServletContextAttributeEvent類繼承自 ServletContextAttribute,並且增加了下面兩個方法分別 用於獲取該屬性的名稱和值:

java.lang.String getName()
java.lang.Object getValue()

servlet頁面

package listener;

import javax.servlet.ServletContextAttributeListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContext;

@WebListener
public class SCAlistener  implements ServletContextAttributeListener{
    
    @Override
    public void attributeAdded(ServletContextAttributeEvent event) {
        String name = event.getName();
        Object  value =  event.getValue();
        
       System.out.println(name + " create success, value " + value);
    }
}

 

三. Session Listeners

  一共有四個HttpSession相關的監聽器接口: HttpSessionListener,HttpSessionActivation Listener、 HttpSessionAttributeListener和 HttpSessionBindingListener。這四個接口都在 javax.servlet.http包中,下面分別對它們進行介紹。

1. HttpSessionListener

  當一個HttpSession創建或者銷毀時,容器都會通知 所有的HttpSessionListener監聽器,HttpSessionListener 接口有兩個方法:sessionCreated和sessionDestroyed:

void sessionCreated(HttpSessionEvent event)
void sessionDestroyed(HttpSessionEvent event)

  這兩個方法都可以接收到一個繼承於java.util.Event 的HttpSessionEvent對象。可以通過調用 HttpSessionEvent對象的getSession方法來獲取當前的 HttpSession。getSession方法如下:

HttpSession getSession()

  舉一個例子,app08a應用中的 SessionListener類。這個監聽器來統計HttpSession的數 量。它使用了一個AtomicInteger對象來統計,並且將這 個對象保存成ServletContext范圍的屬性。每當有一個 HttpSession被創建時,這個AtomicInteger對象就會加 一。每當有一個HttpSession被銷毀時,這個 AtomicInteger對象就會減一。所以這個對象會保存着當 前存活的HttpSession數量。這里使用了AtomicInteger 來 代替Integer類型是為了保證能同步進行加減的操作。

SessionListener 類

package listener;

import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

@WebListener
public class SessionListener implements HttpSessionListener, ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext servletContext = sce.getServletContext();
        servletContext.setAttribute("userCounter", new AtomicInteger());
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    }

    @Override
    public void sessionCreated(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        ServletContext servletContext = session.getServletContext();
        AtomicInteger userCounter = (AtomicInteger) servletContext.getAttribute("userCounter");
        int userCount = userCounter.incrementAndGet();
        System.out.println("userCount incremented to :" + userCount);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        ServletContext servletContext = session.getServletContext();
        AtomicInteger userCounter = (AtomicInteger) servletContext.getAttribute("userCounter");
        int userCount = userCounter.decrementAndGet();
        System.out.println("---------- userCount decremented to:" + userCount);
    }
}

 

 

 

  如清單8.3所示,SessionListener類實現了 ServletContextListener和HttpSessionListener接口。所以 需要實現這兩個接口的所有方法。

   其中繼承自ServletContextListener接口的 contextInitialized方法創建了一個AtomicInteger對象並將 其保存在ServletContext屬性中。由於是在應用啟動的時 候創建,因此這個AtomicInteger對象的初始值為0。這 個ServletContext屬性的名字為userCounter。

  sessionCreated方法在每個HttpSession創建時被調 用。當有HttpSession創建時,從ServletContext中獲取 userCounter屬性。然后調用userCounter的 incrementAndGet 方法讓計數加一。最后在控制台將 userCounter的值打印出來,可以直觀地看到效果。

  sessionDestroyed方法會在HttpSession銷毀之前被調 用。這個方法的實現和sessionCreated類似,只不過對 userCounter改為減一操作。

  可以通過不同的瀏覽器訪問countries.jsp頁面來查 看監聽器的效果,

  用同一個瀏覽器再次訪問這個URL並不會改變 userCounter,因為這屬於同一個HttpSession。使用不同 的瀏覽器訪問才能增加userCounter的值。

  如果你有時間等待HttpSession過期的話,在控制台 也能看到HttpSession銷毀時打印的信息。

 

2. HttpSessionAttributeListener

  HttpSessionAttributeListener接口和 ServletContextAttributeListener類似,它響應的是 HttpSession范圍屬性的添加、刪除和替換。

   HttpSessionAttributeListener接口有以下方法

void attributeAdded(HttpSessionBindingEvent event)
void attributeRemoved( HttpSessionBindingEvent event)
void attributeReplaced( HttpSessionBindingEvent event)

  attributeAdded方法在一個HttpSession范圍屬性被添 加時被容器調用。attributeRemoved方法在一個 HttpSession范圍屬性被刪除時被容器調用。而 attributeReplaced方法在一個HttpSession范圍屬性被新的 替換時被容器調用.

  這三個方法都能獲取到一個 HttpSessionBindingEvent 的對象,通過這個對象可以獲 取屬性的名稱和值:

java.lang.String getName()
java.lang.Object getValue()

  由於HttpSessionBindingEvent是HttpSessionEvent的 子類,因此也可以在HttpSession Attribute Listener 實現 類中獲得HttpSession。

servlet頁面

@WebListener
public class SessionListener implements HttpSessionListener, ServletContextListener ,HttpSessionAttributeListener{
    @Override
    public void attributeAdded(HttpSessionBindingEvent event) {
        String name = event.getName();
        Object value = event.getValue();
        System.out.println("name = " + name + ", value is " + value);
        
    }

 

3. HttpSessionActivationListener

  在分布式環境下,會用多個容器來進行負載均衡, 有可能需要將session保存起來,在容器之間傳遞。例如 當一個容器內存不足時,會把很少用到的對象轉存到其 他容器上。這時候,容器就會通知所有 HttpSessionActivationListener 接口的實現類。

  HttpSessionActivationListener接口有兩個方法, sessionDidActivate和sessionWillPassivate:

void sessionDidActivate(HttpSessionEvent event)
void sessionWillPassivate(HttpSessionEvent event)

  當HttpSession被轉移到其他容器之后, sessionDidActivate方法會被調用。容器將一個 HttpSessionEvent方法傳遞到方法里,可以從這個對象 獲得HttpSession。

  當一個HttpSession將要失效時,容器會調用 sessionWillPassivate方法。和sessionDidActivate方法一 樣,容器將一個HttpSessionEvent方法傳遞到方法里, 可以從這個對象獲得HttpSession。

4. HttpSessionBindingListener

  當有屬性綁定或者解綁到HttpSession上時, HttpSessionBindingListener 監聽器會被調用。如果對 HttpSession屬性的綁定和解綁動作感興趣,就可以實現 HttpSessionBindingListener 來監聽。例如可以在 HttpSession屬性綁定時更新狀態,或者在屬性解綁時釋 放資源。

  Product類就是一個例子。

HttpSessionBindingListener的一個實現類

package model;

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

public class Product implements HttpSessionBindingListener {
    private String id;
    private String name;
    private double price;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public void valueBound(HttpSessionBindingEvent event) { //當有HttpSessionBindingEvent 發生時會自動調用
        String attributeName = event.getName();
        System.out.println(attributeName + " valueBound");
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        String attributeName = event.getName();
        System.out.println(attributeName + " valueUnbound");
    }
}

jsp頁面

<%
    model.Product product = new Product();
    product.setId("1");
    product.setName("d");
    product.setPrice(1.5);
    session.setAttribute("Product",product);
%>
</body>

四. ServletRequest Listeners

  ServletRequest范圍的監聽器接口有三個: ServletRequestListener、ServletRequestAttribute Listener 和AsyncListener。

1.  ServletRequestListener

  ServletRequestListener監聽器會對ServletRequest的 創建和銷毀事件進行響應。容器會通過一個池來存放並 重復利用多個ServletRequest,ServletRequest的創建是 從容器池里被分配出來的時刻開始,而它的銷毀時刻是 放回容器池里的時間。

  ServletRequestListener 接口有兩個方法, requestInitialized和requestDestroyed:

void requestInitialized(ServletRequestEvent event)
void requestDestroyed(ServletRequestEvent event)

  當一個ServletRequest創建(從容器池里取出) 時,requestInitialized方法會被調用,當ServletRequest銷 毀(被容器回收)時,requestDestroyed方法會被調用。 這兩個方法都會接收到一個ServletRequestEvent對象, 可以通過使用這個對象的getServletRequest方法來獲取ServletRequest對象:

  

ServletRequest getServletRequest()

  另外,ServletRequestEvent接口也提供了一個 getServletContext方法來獲取ServletContext,如下所 示:

ServletContext getServletContext()

例: PerfStatListener類。這個監聽器用來計算每個ServletRequest從創建到銷毀的生存時間。

  PerfStatListener實現了 ServletRequestListener接口,來計算每個HTTP請求的完 成時間。由於容器在請求創建時會調用 ServletRequestListener的requestInitialized方法,在銷毀 時會調用requestDestroyed,因此很容易就可以計算出時 間。只需要在記錄下兩個事件的事件,並且相減,就可 以計算出一次HTTP請求的完成時間了。

PerfStatListener類

package listener;

import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;

@WebListener
public class PerfStatListener implements ServletRequestListener {
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        ServletRequest servletRequest = sre.getServletRequest();
        servletRequest.setAttribute("start", System.nanoTime());
    }

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        ServletRequest servletRequest = sre.getServletRequest();
        Long start = (Long) servletRequest.getAttribute("start");
        Long end = System.nanoTime();
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String uri = httpServletRequest.getRequestURI();
        System.out.println("time taken to execute " + uri + ":" + ((end - start) / 1000) + "microseconds");
    }
}

  requestInitialized 方法調用 System.nanoTime()獲取當前系統時間的數值(Long類 型),並將這個數值保存到ServletRequest中.

  nanoTime返回一個long類型的數值來表示任意時 間。這個數值和系統或是時鍾時間都沒什么關系,但是 同一個JVM上調用兩次nanoTime得到的數值可以計算出兩次調用之間的時間。

  所以,在requestDestroyed方法中再次調用 nanoTime方法,並且減去第一次調用獲得的數值,就 得到HTTP請求的完成時間了:

效果

2. ServletRequestAttributeListener

  當一個ServletRequest范圍的屬性被添加、刪除或 替換時,ServletRequestAttributeListener接口會被調用。 ServletRequestAttributeListener接口提供了三個方法: attributeAdded、attribute Replaced和attributeRemoved。 如下所示:

void attributeAdded(ServletRequestAttributeEvent event)
void attributeRemoved(ServletRequestAttributeEvent event)
void attributeReplaced(ServletRequestAttributeEvent event)

  這些方法都可以獲得一個繼承自 ServletRequestEvent的ServletRequestAttributeEvent對 象。通過ServletRequestAttributeEvent類提供的getName 和getValue方法可以訪問到屬性的名稱和值:

java.lang.String getName()
java.lang.Object getValue()

例;

@WebListener
public class SessionListener implements HttpSessionListener, ServletContextListener ,HttpSessionAttributeListener,
                ServletRequestAttributeListener{
    @Override
    public void attributeAdded(ServletRequestAttributeEvent event) {
        System.out.println("I was called............");
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM