1. 程式人生 > >Servlet——監聽器Listener詳解

Servlet——監聽器Listener詳解

一、簡介

(一)概述

1、Listener 用於監聽 Java web程式中的事件,例如建立、修改、刪除Session、request、context等,並觸發響應的事件。

2、 Listener 對應觀察者模式,事件發生的時候會自動觸發該事件對應的Listeer。 Listener 主要用於對 Session、request、context 進行監控。servlet2.5 規範中共有 8 種Listener  。

(二)實現

1、不同功能的Listener 需要實現不同的 Listener  介面,一個Listener也可以實現多個介面,這樣就可以多種功能的監聽器一起工作。

2、8種監聽器可以分為三類:

1)監聽 Session、request、context 的創建於銷燬,分別為  

HttpSessionLister、ServletContextListener、ServletRequestListener

2)監聽物件屬性變化,分別為:

HttpSessionAttributeLister、ServletContextAttributeListener、ServletRequestAttributeListener 

3)監聽Session 內的物件,分別為HttpSessionBindingListener 和 HttpSessionActivationListener。與上面六類不同,這兩類 Listener 監聽的是Session 內的物件,而非 Session 本身,不需要在 web.xml中配置。

2、實現web.xml的Listener配置。

1)<listener>標籤與 <listener-class>

2)<listener>一般配置在 <servlet>便籤的前面。

  1. package servlet.listener;  
  2. import javax.servlet.ServletContextEvent;  
  3. import javax.servlet.ServletContextListener;  
  4. /** 
  5.  *  
  6.  * MyListener.java 
  7.  * 
  8.  * @title Context監聽器 
  9.  * @description
     
  10.  * @author SAM-SHO  
  11.  * @Date 2014-9-25 
  12.  */
  13. publicclass MyListener implements ServletContextListener {  
  14.     publicvoid contextDestroyed(ServletContextEvent sce) {  
  15.     }  
  16.     publicvoid contextInitialized(ServletContextEvent sce) {  
  17.     }  
  18. }  

  1. <!--監聽器 -->
  2. <listener>
  3.     <listener-class>servlet.listener.MyListener</listener-class>
  4. </listener>

二、八種類型監聽器

(一)監聽 Session、request、context 的創建於銷燬。

HttpSessionLister、ServletContextListener、ServletRequestListener

1、三種監聽器的觸發時機及使用:


2、例項:實現監聽物件的建立與銷燬

  1. package servlet.listener;  
  2. import javax.servlet.ServletContext;  
  3. import javax.servlet.ServletContextEvent;  
  4. import javax.servlet.ServletContextListener;  
  5. import javax.servlet.ServletRequestEvent;  
  6. import javax.servlet.ServletRequestListener;  
  7. import javax.servlet.http.HttpServletRequest;  
  8. import javax.servlet.http.HttpSession;  
  9. import javax.servlet.http.HttpSessionEvent;  
  10. import javax.servlet.http.HttpSessionListener;  
  11. import org.apache.commons.logging.Log;  
  12. import org.apache.commons.logging.LogFactory;  
  13. /** 
  14.  *  
  15.  * ListenerTest.java 
  16.  *  
  17.  * @title 監聽物件的建立與銷燬 
  18.  * @description 
  19.  * @author SAM-SHO 
  20.  * @Date 2014-12-10 
  21.  */
  22. publicclass ListenerTest implements HttpSessionListener, ServletContextListener, ServletRequestListener {  
  23.     Log log = LogFactory.getLog(getClass());  
  24.     // 建立 session
  25.     publicvoid sessionCreated(HttpSessionEvent se) {  
  26.         HttpSession session = se.getSession();  
  27.         log.info("新建立一個session, ID為: " + session.getId());  
  28.     }  
  29.     // 銷燬 session
  30.     publicvoid sessionDestroyed(HttpSessionEvent se) {  
  31.         HttpSession session = se.getSession();  
  32.         log.info("銷燬一個session, ID為: " + session.getId());  
  33.     }  
  34.     // 載入 context
  35.     publicvoid contextInitialized(ServletContextEvent sce) {  
  36.         ServletContext servletContext = sce.getServletContext();  
  37.         log.info("即將啟動" + servletContext.getContextPath());  
  38.     }  
  39.     // 解除安裝 context
  40.     publicvoid contextDestroyed(ServletContextEvent sce) {  
  41.         ServletContext servletContext = sce.getServletContext();  
  42.         log.info("即將關閉" + servletContext.getContextPath());  
  43.     }  
  44.     // 建立 request
  45.     publicvoid requestInitialized(ServletRequestEvent sre) {  
  46.         HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();  
  47.         String uri = request.getRequestURI();  
  48.         uri = request.getQueryString() == null ? uri : (uri + "?" + request.getQueryString());  
  49.         request.setAttribute("dateCreated", System.currentTimeMillis());  
  50.         log.info("IP " + request.getRemoteAddr() + " 請求 " + uri);  
  51.     }  
  52.     // 銷燬 request
  53.     publicvoid requestDestroyed(ServletRequestEvent sre) {  
  54.         HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();  
  55.         long time = System.currentTimeMillis() - (Long) request.getAttribute("dateCreated");  
  56.         log.info(request.getRemoteAddr() + "請求處理結束, 用時" + time + "毫秒. ");  
  57.     }  
  58. }  


(二)監聽物件屬性變化,分別為HttpSessionAttributeLister、ServletContextAttributeListener、ServletRequestAttributeListener 

1、三種監聽器的觸發時機及使用:


2、例項:實現物件屬性的監聽
  1. package servlet.listener;  
  2. import javax.servlet.http.HttpSession;  
  3. import javax.servlet.http.HttpSessionAttributeListener;  
  4. import javax.servlet.http.HttpSessionBindingEvent;  
  5. import org.apache.commons.logging.Log;  
  6. import org.apache.commons.logging.LogFactory;  
  7. /** 
  8.  *  
  9.  * SessionAttributeListenerTest.java 
  10.  * 
  11.  * @title 監聽Session物件的屬性 
  12.  * @description 
  13.  * @author SAM-SHO  
  14.  * @Date 2014-12-10 
  15.  */
  16. publicclass SessionAttributeListenerTest implements HttpSessionAttributeListener {  
  17.     Log log = LogFactory.getLog(getClass());  
  18.     // 新增屬性
  19.     publicvoid attributeAdded(HttpSessionBindingEvent se) {  
  20.         HttpSession session = se.getSession();  
  21.         String name = se.getName();  
  22.         log.info("新建session屬性:" + name + ", 值為:" + se.getValue());  
  23.     }  
  24.     // 刪除屬性
  25.     publicvoid attributeRemoved(HttpSessionBindingEvent se) {  
  26.         HttpSession session = se.getSession();  
  27.         String name = se.getName();  
  28.         log.info("刪除session屬性:" + name + ", 值為:" + se.getValue());  
  29.     }  
  30.     // 修改屬性
  31.     publicvoid attributeReplaced(HttpSessionBindingEvent se) {  
  32.         HttpSession session = se.getSession();  
  33.         String name = se.getName();  
  34.         Object oldValue = se.getValue();  
  35.         log.info("修改session屬性:" + name + ", 原值:" + oldValue + ", 新值:" + session.getAttribute(name));  
  36.     }  
  37. }  


(三)監聽Session 內的物件,分別為HttpSessionBindingListener 和 HttpSessionActivationListener

1、觸發時機及使用:物件必須實現Listener介面,不需要在web.xml中配置


2、例項:實現物件屬性的監聽

  1. package servlet.listener;  
  2. import java.io.Serializable;  
  3. import java.util.Date;  
  4. import javax.servlet.http.HttpSession;  
  5. import javax.servlet.http.HttpSessionActivationListener;  
  6. import javax.servlet.http.HttpSessionBindingEvent;  
  7. import javax.servlet.http.HttpSessionBindingListener;  
  8. import javax.servlet.http.HttpSessionEvent;  
  9. import org.apache.commons.logging.Log;  
  10. import org.apache.commons.logging.LogFactory;  
  11. /** 
  12.  *  
  13.  * PersonInfo.java 
  14.  * 
  15.  * @title 同時實現多個介面 
  16.  * 被序列化,需要實現Serializable介面 
  17.  * @description 
  18.  * @author SAM-SHO  
  19.  * @Date 2014-12-10 
  20.  */
  21. publicclass PersonInfo implements HttpSessionActivationListener, HttpSessionBindingListener, Serializable {  
  22.     privatestaticfinallong serialVersionUID = -4780592776386225973L;  
  23.     Log log = LogFactory.getLog(getClass());  
  24.     private String name;  
  25.     private Date dateCreated;  
  26.     // 從硬碟載入後
  27.     publicvoid sessionDidActivate(HttpSessionEvent se) {  
  28.         HttpSession session = se.getSession();  
  29.         log.info(this + "已經成功從硬碟中載入。sessionId: " + session.getId());  
  30.     }  
  31.     // 即將被鈍化到硬碟時
  32.     publicvoid sessionWillPassivate(HttpSessionEvent se) {  
  33.         HttpSession session = se.getSession();  
  34.         log.info(this + "即將儲存到硬碟。sessionId: " + session.getId());  
  35.     }  
  36.     // 被放進session前
  37.     publicvoid valueBound(HttpSessionBindingEvent event) {  
  38.         HttpSession session = event.getSession();  
  39.         String name = event.getName();  
  40.         log.info(this + "被繫結到session \"" + session.getId() + "\"的" + name + "屬性上");  
  41.         // 記錄放到session中的時間
  42.         this.setDateCreated(new Date());  
  43.     }  
  44.     // 從session中移除後
  45.     publicvoid valueUnbound(HttpSessionBindingEvent event) {  
  46.         HttpSession session = event.getSession();  
  47.         String name = event.getName();  
  48.         log.info(this + "被從session \"" + session.getId() + "\"的" + name + "屬性上移除");  
  49.     }  
  50.     @Override
  51.     public String toString() {  
  52.         return"PersonInfo(" + name + ")";  
  53.     }  
  54.     public String getName() {  
  55.         return name;  
  56.     }  
  57.     publicvoid setName(String name) {  
  58.         this.name = name;  
  59.     }  
  60.     public Date getDateCreated() {  
  61.         return dateCreated;  
  62.     }  
  63.     publicvoid setDateCreated(Date dateCreated) {  
  64.         this.dateCreated = dateCreated;  
  65.     }  
  66. }  


三、Listener 例項

(一)單態登入:一個賬號只能在一臺機器上登入。

1、Listener 的程式碼:

  1. package servlet.listener.singleton;  
  2. import java.util.HashMap;  
  3. import java.util.Map;  
  4. import javax.servlet.http.HttpSession;  
  5. import javax.servlet.http.HttpSessionAttributeListener;  
  6. import javax.servlet.http.HttpSessionBindingEvent;  
  7. import org.apache.commons.logging.Log;  
  8. import org.apache.commons.logging.LogFactory;  
  9. /** 
  10.  *  
  11.  * LoginSessionListener.java 
  12.  * 
  13.  * @title 實現單態登入的監聽器 
  14.  * @description 
  15.  * @author SAM-SHO  
  16.  * @Date 2014-12-10 
  17.  */
  18. publicclass LoginSessionListener implements HttpSessionAttributeListener {  
  19.     Log log = LogFactory.getLog(this.getClass());  
  20.     Map<String, HttpSession> map = new HashMap<String, HttpSession>();  
  21.     publicvoid attributeAdded(HttpSessionBindingEvent event) {  
  22.         String name = event.getName();  
  23.         // 登入
  24.         if (name.equals("personInfo")) {  
  25.             PersonInfo personInfo = (PersonInfo) event.getValue();  
  26.             if (map.get(personInfo.getAccount()) != null) {  
  27.                 // map 中有記錄,表明該帳號在其他機器上登入過,將以前的登入失效
  28.                 HttpSession session = map.get(personInfo.getAccount());  
  29.                 PersonInfo oldPersonInfo = (PersonInfo) session.getAttribute("personInfo");//map已經存在的舊的資訊
  30.                 log.info("帳號" + oldPersonInfo.getAccount() + "在" + oldPersonInfo.getIp() + "已經登入,該登入將被迫下線。");  
  31.                 session.removeAttribute("personInfo");  
  32.                 session.setAttribute("msg""您的帳號已經在其他機器上登入,您被迫下線。");  
  33.             }  
  34.             // 將session以使用者名稱為索引,放入map中
  35.             map.put(personInfo.getAccount(), event.getSession());  
  36.             log.info("帳號" + personInfo.getAccount() + "在" + personInfo.getIp() + "登入。");  
  37.         }  
  38.     }  
  39.     publicvoid attributeRemoved(HttpSessionBindingEvent event) {  
  40.         String name = event.getName();  
  41.         // 登出
  42.         if (name.equals("personInfo")) {  
  43.             // 將該session從map中移除
  44.             PersonInfo personInfo = (PersonInfo) event.getValue();  
  45.             map.remove(personInfo.getAccount());  
  46.             log.info("帳號" + personInfo.getAccount() + "登出。");  
  47.         }  
  48.     }  
  49.     publicvoid attributeReplaced(HttpSessionBindingEvent event) {  
  50.         String name = event.getName();  
  51.         // 沒有登出的情況下,用另一個帳號登入
  52.         if (name.equals("personInfo")) {  
  53.             // 移除舊的的登入資訊
  54.             PersonInfo oldPersonInfo = (PersonInfo) event.getValue();  
  55.             map.remove(oldPersonInfo.getAccount());  
  56.             // 新的登入資訊
  57.             PersonInfo personInfo = (PersonInfo) event.getSession().getAttribute("personInfo");  
  58.             // 也要檢查新登入的帳號是否在別的機器上登入過
  59.             if (map.get(personInfo.getAccount()) != null) {  
  60.                 // map 中有記錄,表明該帳號在其他機器上登入過,將以前的登入失效
  61.                 HttpSession session = map.get(personInfo.getAccount());  
  62.                 session.removeAttribute("personInfo");  
  63.                 session.setAttribute("msg""您的帳號已經在其他機器上登入,您被迫下線。");  
  64.             }  
  65.             map.put("personInfo", event.getSession());  
  66.         }  
  67.     }  
  68. }  

  1. package servlet.listener.singleton;  
  2. import java.io.Serializable;  
  3. import java.util.Date;  
  4. /** 
  5.  *  
  6.  * PersonInfo.java 
  7.  * 
  8.  * @title  
  9.  * @description 
  10.  * @author SAM-SHO  
  11.  * @Date 2014-12-10 
  12.  */
  13. publicclass PersonInfo implements Serializable {  
  14.     privatestaticfinallong serialVersionUID = 4063725584941336123L;  
  15.     // 帳號
  16.     private String account;  
  17.     // 登入IP地址
  18.     private String ip;  
  19.     // 登入時間
  20.     private Date loginDate;  
  21.     public String getAccount() {  
  22.         return account;  
  23.     }  
  24.     publicvoid setAccount(String account) {  
  25.         this.account = account;  
  26.     }  
  27.     public String getIp() {  
  28.         return ip;  
  29.     }  
  30.     publicvoid setIp(String ip) {  
  31.         this.ip = ip;  
  32.     }  
  33.     public Date getLoginDate() {  
  34.         return loginDate;  
  35.     }  
  36.     publicvoid setLoginDate(Date loginDate) {  
  37.         this.loginDate = loginDate;  
  38.     }  
  39.     @Override
  40.     publicint hashCode() {  
  41.         finalint prime = 31;  
  42.         int result = 1;  
  43.         result = prime * result + ((account == null) ? 0 : account.hashCode());  
  44.         result = prime * result + ((ip == null) ? 0 : ip.hashCode());  
  45.         return result;  
  46.     }  
  47.     @Override
  48.     publicboolean equals(Object obj) {  
  49.         if (this == obj)  
  50.             returntrue;  
  51.         if (obj == null)  
  52.             returnfalse;  
  53.         if (getClass() != obj.getClass())  
  54.             returnfalse;  
  55.         PersonInfo other = (PersonInfo) obj;  
  56.         if (account == null) {  
  57.             if (other.account != null)  
  58.                 returnfalse;  
  59.         } elseif (!account.equals(other.account))  
  60.             returnfalse;  
  61.         if (ip == null) {  
  62.             if (other.ip != null)  
  63.                 returnfalse;  
  64.         } elseif (!ip.equals(other.ip))  
  65.             returnfalse;  
  66.         returntrue;  
  67.     }  
  68. }  

  1. <!-- 單態登入監聽器 -->
  2. <listener>
  3.     <listener-class>servlet.listener.singleton.LoginSessionListener</listener-class>
  4. </listener>


(二)顯示線上人數:會需要3個監聽器。

1、ContextListener:獲取服務啟動的時間等。

2、RequestListener:獲取客戶端的IP、訪問地址,訪問次數等。

3、SessionListener:需要監聽 Session 的建立與屬性變化。

4、程式碼如下:

  1. package com.helloweenvsfei.listener;  
  2. import java.util.Date;  
  3. import javax.servlet.ServletContextEvent;  
  4. import javax.servlet.ServletContextListener;  
  5. import com.helloweenvsfei.util.ApplicationConstants;  
  6. publicclass MyContextListener implements ServletContextListener {  
  7.     publicvoid contextInitialized(ServletContextEvent event) {  
  8.         // 啟動時,記錄伺服器啟動時間
  9.         ApplicationConstants.START_DATE = new Date();  
  10.     }  
  11.     publicvoid contextDestroyed(ServletContextEvent event) {  
  12.         // 關閉時,將結果清除。也可以將結果儲存到硬碟上。
  13.         ApplicationConstants.START_DATE = null;  
  14.         ApplicationConstants.MAX_ONLINE_COUNT_DATE = null;  
  15.     }  
  16. }  


  1. package com.helloweenvsfei.listener;  
  2. import javax.servlet.ServletRequestEvent;  
  3. import javax.servlet.ServletRequestListener;  
  4. import javax.servlet.http.HttpServletRequest;  
  5. import javax.servlet.http.HttpSession;  
  6. publicclass MyRequestListener implements ServletRequestListener {  
  7.     publicvoid requestDestroyed(ServletRequestEvent event) {  
  8.     }  
  9.     publicvoid requestInitialized(ServletRequestEvent event) {  
  10.         HttpServletRequest request = (HttpServletRequest) event  
  11.                 .getServletRequest();  
  12.         HttpSession session = request.getSession(true);  
  13.         // 記錄IP地址
  14.         session.setAttribute("ip", request.getRemoteAddr());  
  15.         // 記錄訪問次數,只記錄訪問 .html, .do, .jsp, .action 的累計次數
  16.         String uri = request.getRequestURI();  
  17.         String[] suffix = { ".html"".do"".jsp"".action" };  
  18.         for (int i=0; i<suffix.length; i++) {  
  19.             if (uri.endsWith(suffix[i])) {  
  20.                 break;  
  21.             }  
  22.             if(i == suffix.length-1)  
  23.                 return;  
  24.         }  
  25.         Integer activeTimes = (Integer) session.getAttribute("activeTimes");  
  26.         if (activeTimes == null) {  
  27.             activeTimes = 0;  
  28.         }  
  29.         session.setAttribute("activeTimes", activeTimes + 1);  
  30.     }  
  31. }  

  1. package com.helloweenvsfei.listener;  
  2. import java.util.Date;  
  3. import javax.servlet.http.HttpSession;  
  4. import javax.servlet.http.HttpSessionAttributeListener;  
  5. import javax.servlet.http.HttpSessionBindingEvent;  
  6. import javax.servlet.http.HttpSessionEvent;  
  7. import javax.servlet.http.HttpSessionListener;  
  8. import com.helloweenvsfei.util.ApplicationConstants;  
  9. publicclass MySessionListener implements HttpSessionListener,  
  10.         HttpSessionAttributeListener {  
  11.     publicvoid sessionCreated(HttpSessionEvent sessionEvent) {  
  12.         HttpSession session = sessionEvent.getSession();  
  13.         // 將 session 放入 map
  14.         ApplicationConstants.SESSION_MAP.put(session.getId(), session);  
  15.         // 總訪問人數++
  16.         ApplicationConstants.TOTAL_HISTORY_COUNT++;  
  17.         // 如果當前線上人數超過歷史記錄,則更新最大線上人數,並記錄時間
  18.         if (ApplicationConstants.SESSION_MAP.size() > ApplicationConstants.MAX_ONLINE_COUNT) {  
  19.             ApplicationConstants.MAX_ONLINE_COUNT = ApplicationConstants.SESSION_MAP  
  20.                     .size();  
  21.             ApplicationConstants.MAX_ONLINE_COUNT_DATE = new Date();  
  22.         }  
  23.     }  
  24.     publicvoid sessionDestroyed(HttpSessionEvent sessionEvent) {  
  25.         HttpSession session = sessionEvent.getSession();  
  26.         // 將session從map中移除
  27.         ApplicationConstants.SESSION_MAP.remove(session.getId());  
  28.     }  
  29.     publicvoid attributeAdded(HttpSessionBindingEvent event) {  
  30.         if (event.getName().equals("personInfo")) {  
  31.             // 當前登入使用者數++
  32.             ApplicationConstants.CURRENT_LOGIN_COUNT++;  
  33.             HttpSession session = event.getSession();  
  34.             // 查詢該帳號有沒有在其他機器上登入
  35.             for (HttpSession sess : ApplicationConstants.SESSION_MAP.values()) {  
  36.                 // 如果該帳號已經在其他機器上登入,則以前的登入失效
  37.                 if (event.getValue().equals(sess.getAttribute("personInfo"))  
  38.                         && session.getId() != sess.getId()) {  
  39.                     sess.invalidate();  
  40.                 }  
  41.             }  
  42.         }  
  43.     }  
  44.     publicvoid attributeRemoved(HttpSessionBindingEvent event) {  
  45.         // 登出 當前登入使用者數--
  46.         if (event.getName().equals("personInfo")) {  
  47.             ApplicationConstants.CURRENT_LOGIN_COUNT--;  
  48.         }  
  49.     }  
  50.     publicvoid attributeReplaced(HttpSessionBindingEvent event) {  
  51.         // 重新登入
  52.         if (event.getName().equals("personInfo")) {  
  53.             HttpSession session = event.getSession();  
  54.             for (HttpSession sess : ApplicationConstants.SESSION_MAP.values()) {  
  55.                 // 如果新帳號在其他機器上登入過,則以前登入失效
  56.                 if (event.getValue().equals(sess.getAttribute("personInfo"))  
  57.                         && session.getId() != sess.getId()) {  
  58.                     sess.invalidate();  
  59.                 }  
  60.             }  
  61.         }  
  62.     }