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>便籤的前面。
- package servlet.listener;
- import javax.servlet.ServletContextEvent;
- import javax.servlet.ServletContextListener;
- /**
- *
- * MyListener.java
- *
- * @title Context監聽器
- * @description
- * @author SAM-SHO
- * @Date 2014-9-25
- */
- publicclass MyListener implements ServletContextListener {
- publicvoid contextDestroyed(ServletContextEvent sce) {
- }
- publicvoid contextInitialized(ServletContextEvent sce) {
- }
- }
- <!--監聽器 -->
- <listener>
- <listener-class>servlet.listener.MyListener</listener-class>
- </listener>
二、八種類型監聽器
(一)監聽 Session、request、context 的創建於銷燬。
HttpSessionLister、ServletContextListener、ServletRequestListener
1、三種監聽器的觸發時機及使用:
2、例項:實現監聽物件的建立與銷燬
- package servlet.listener;
- import javax.servlet.ServletContext;
- import javax.servlet.ServletContextEvent;
- import javax.servlet.ServletContextListener;
- import javax.servlet.ServletRequestEvent;
- import javax.servlet.ServletRequestListener;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpSession;
- import javax.servlet.http.HttpSessionEvent;
- import javax.servlet.http.HttpSessionListener;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- /**
- *
- * ListenerTest.java
- *
- * @title 監聽物件的建立與銷燬
- * @description
- * @author SAM-SHO
- * @Date 2014-12-10
- */
- publicclass ListenerTest implements HttpSessionListener, ServletContextListener, ServletRequestListener {
- Log log = LogFactory.getLog(getClass());
- // 建立 session
- publicvoid sessionCreated(HttpSessionEvent se) {
- HttpSession session = se.getSession();
- log.info("新建立一個session, ID為: " + session.getId());
- }
- // 銷燬 session
- publicvoid sessionDestroyed(HttpSessionEvent se) {
- HttpSession session = se.getSession();
- log.info("銷燬一個session, ID為: " + session.getId());
- }
- // 載入 context
- publicvoid contextInitialized(ServletContextEvent sce) {
- ServletContext servletContext = sce.getServletContext();
- log.info("即將啟動" + servletContext.getContextPath());
- }
- // 解除安裝 context
- publicvoid contextDestroyed(ServletContextEvent sce) {
- ServletContext servletContext = sce.getServletContext();
- log.info("即將關閉" + servletContext.getContextPath());
- }
- // 建立 request
- publicvoid requestInitialized(ServletRequestEvent sre) {
- HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
- String uri = request.getRequestURI();
- uri = request.getQueryString() == null ? uri : (uri + "?" + request.getQueryString());
- request.setAttribute("dateCreated", System.currentTimeMillis());
- log.info("IP " + request.getRemoteAddr() + " 請求 " + uri);
- }
- // 銷燬 request
- publicvoid requestDestroyed(ServletRequestEvent sre) {
- HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
- long time = System.currentTimeMillis() - (Long) request.getAttribute("dateCreated");
- log.info(request.getRemoteAddr() + "請求處理結束, 用時" + time + "毫秒. ");
- }
- }
(二)監聽物件屬性變化,分別為HttpSessionAttributeLister、ServletContextAttributeListener、ServletRequestAttributeListener
1、三種監聽器的觸發時機及使用:
- package servlet.listener;
- import javax.servlet.http.HttpSession;
- import javax.servlet.http.HttpSessionAttributeListener;
- import javax.servlet.http.HttpSessionBindingEvent;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- /**
- *
- * SessionAttributeListenerTest.java
- *
- * @title 監聽Session物件的屬性
- * @description
- * @author SAM-SHO
- * @Date 2014-12-10
- */
- publicclass SessionAttributeListenerTest implements HttpSessionAttributeListener {
- Log log = LogFactory.getLog(getClass());
- // 新增屬性
- publicvoid attributeAdded(HttpSessionBindingEvent se) {
- HttpSession session = se.getSession();
- String name = se.getName();
- log.info("新建session屬性:" + name + ", 值為:" + se.getValue());
- }
- // 刪除屬性
- publicvoid attributeRemoved(HttpSessionBindingEvent se) {
- HttpSession session = se.getSession();
- String name = se.getName();
- log.info("刪除session屬性:" + name + ", 值為:" + se.getValue());
- }
- // 修改屬性
- publicvoid attributeReplaced(HttpSessionBindingEvent se) {
- HttpSession session = se.getSession();
- String name = se.getName();
- Object oldValue = se.getValue();
- log.info("修改session屬性:" + name + ", 原值:" + oldValue + ", 新值:" + session.getAttribute(name));
- }
- }
(三)監聽Session 內的物件,分別為HttpSessionBindingListener 和 HttpSessionActivationListener
1、觸發時機及使用:物件必須實現Listener介面,不需要在web.xml中配置
2、例項:實現物件屬性的監聽
- package servlet.listener;
- import java.io.Serializable;
- import java.util.Date;
- import javax.servlet.http.HttpSession;
- import javax.servlet.http.HttpSessionActivationListener;
- import javax.servlet.http.HttpSessionBindingEvent;
- import javax.servlet.http.HttpSessionBindingListener;
- import javax.servlet.http.HttpSessionEvent;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- /**
- *
- * PersonInfo.java
- *
- * @title 同時實現多個介面
- * 被序列化,需要實現Serializable介面
- * @description
- * @author SAM-SHO
- * @Date 2014-12-10
- */
- publicclass PersonInfo implements HttpSessionActivationListener, HttpSessionBindingListener, Serializable {
- privatestaticfinallong serialVersionUID = -4780592776386225973L;
- Log log = LogFactory.getLog(getClass());
- private String name;
- private Date dateCreated;
- // 從硬碟載入後
- publicvoid sessionDidActivate(HttpSessionEvent se) {
- HttpSession session = se.getSession();
- log.info(this + "已經成功從硬碟中載入。sessionId: " + session.getId());
- }
- // 即將被鈍化到硬碟時
- publicvoid sessionWillPassivate(HttpSessionEvent se) {
- HttpSession session = se.getSession();
- log.info(this + "即將儲存到硬碟。sessionId: " + session.getId());
- }
- // 被放進session前
- publicvoid valueBound(HttpSessionBindingEvent event) {
- HttpSession session = event.getSession();
- String name = event.getName();
- log.info(this + "被繫結到session \"" + session.getId() + "\"的" + name + "屬性上");
- // 記錄放到session中的時間
- this.setDateCreated(new Date());
- }
- // 從session中移除後
- publicvoid valueUnbound(HttpSessionBindingEvent event) {
- HttpSession session = event.getSession();
- String name = event.getName();
- log.info(this + "被從session \"" + session.getId() + "\"的" + name + "屬性上移除");
- }
- @Override
- public String toString() {
- return"PersonInfo(" + name + ")";
- }
- public String getName() {
- return name;
- }
- publicvoid setName(String name) {
- this.name = name;
- }
- public Date getDateCreated() {
- return dateCreated;
- }
- publicvoid setDateCreated(Date dateCreated) {
- this.dateCreated = dateCreated;
- }
- }
三、Listener 例項
(一)單態登入:一個賬號只能在一臺機器上登入。
1、Listener 的程式碼:
- package servlet.listener.singleton;
- import java.util.HashMap;
- import java.util.Map;
- import javax.servlet.http.HttpSession;
- import javax.servlet.http.HttpSessionAttributeListener;
- import javax.servlet.http.HttpSessionBindingEvent;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- /**
- *
- * LoginSessionListener.java
- *
- * @title 實現單態登入的監聽器
- * @description
- * @author SAM-SHO
- * @Date 2014-12-10
- */
- publicclass LoginSessionListener implements HttpSessionAttributeListener {
- Log log = LogFactory.getLog(this.getClass());
- Map<String, HttpSession> map = new HashMap<String, HttpSession>();
- publicvoid attributeAdded(HttpSessionBindingEvent event) {
- String name = event.getName();
- // 登入
- if (name.equals("personInfo")) {
- PersonInfo personInfo = (PersonInfo) event.getValue();
- if (map.get(personInfo.getAccount()) != null) {
- // map 中有記錄,表明該帳號在其他機器上登入過,將以前的登入失效
- HttpSession session = map.get(personInfo.getAccount());
- PersonInfo oldPersonInfo = (PersonInfo) session.getAttribute("personInfo");//map已經存在的舊的資訊
- log.info("帳號" + oldPersonInfo.getAccount() + "在" + oldPersonInfo.getIp() + "已經登入,該登入將被迫下線。");
- session.removeAttribute("personInfo");
- session.setAttribute("msg", "您的帳號已經在其他機器上登入,您被迫下線。");
- }
- // 將session以使用者名稱為索引,放入map中
- map.put(personInfo.getAccount(), event.getSession());
- log.info("帳號" + personInfo.getAccount() + "在" + personInfo.getIp() + "登入。");
- }
- }
- publicvoid attributeRemoved(HttpSessionBindingEvent event) {
- String name = event.getName();
- // 登出
- if (name.equals("personInfo")) {
- // 將該session從map中移除
- PersonInfo personInfo = (PersonInfo) event.getValue();
- map.remove(personInfo.getAccount());
- log.info("帳號" + personInfo.getAccount() + "登出。");
- }
- }
- publicvoid attributeReplaced(HttpSessionBindingEvent event) {
- String name = event.getName();
- // 沒有登出的情況下,用另一個帳號登入
- if (name.equals("personInfo")) {
- // 移除舊的的登入資訊
- PersonInfo oldPersonInfo = (PersonInfo) event.getValue();
- map.remove(oldPersonInfo.getAccount());
- // 新的登入資訊
- PersonInfo personInfo = (PersonInfo) event.getSession().getAttribute("personInfo");
- // 也要檢查新登入的帳號是否在別的機器上登入過
- if (map.get(personInfo.getAccount()) != null) {
- // map 中有記錄,表明該帳號在其他機器上登入過,將以前的登入失效
- HttpSession session = map.get(personInfo.getAccount());
- session.removeAttribute("personInfo");
- session.setAttribute("msg", "您的帳號已經在其他機器上登入,您被迫下線。");
- }
- map.put("personInfo", event.getSession());
- }
- }
- }
- package servlet.listener.singleton;
- import java.io.Serializable;
- import java.util.Date;
- /**
- *
- * PersonInfo.java
- *
- * @title
- * @description
- * @author SAM-SHO
- * @Date 2014-12-10
- */
- publicclass PersonInfo implements Serializable {
- privatestaticfinallong serialVersionUID = 4063725584941336123L;
- // 帳號
- private String account;
- // 登入IP地址
- private String ip;
- // 登入時間
- private Date loginDate;
- public String getAccount() {
- return account;
- }
- publicvoid setAccount(String account) {
- this.account = account;
- }
- public String getIp() {
- return ip;
- }
- publicvoid setIp(String ip) {
- this.ip = ip;
- }
- public Date getLoginDate() {
- return loginDate;
- }
- publicvoid setLoginDate(Date loginDate) {
- this.loginDate = loginDate;
- }
- @Override
- publicint hashCode() {
- finalint prime = 31;
- int result = 1;
- result = prime * result + ((account == null) ? 0 : account.hashCode());
- result = prime * result + ((ip == null) ? 0 : ip.hashCode());
- return result;
- }
- @Override
- publicboolean equals(Object obj) {
- if (this == obj)
- returntrue;
- if (obj == null)
- returnfalse;
- if (getClass() != obj.getClass())
- returnfalse;
- PersonInfo other = (PersonInfo) obj;
- if (account == null) {
- if (other.account != null)
- returnfalse;
- } elseif (!account.equals(other.account))
- returnfalse;
- if (ip == null) {
- if (other.ip != null)
- returnfalse;
- } elseif (!ip.equals(other.ip))
- returnfalse;
- returntrue;
- }
- }
- <!-- 單態登入監聽器 -->
- <listener>
- <listener-class>servlet.listener.singleton.LoginSessionListener</listener-class>
- </listener>
(二)顯示線上人數:會需要3個監聽器。
1、ContextListener:獲取服務啟動的時間等。
2、RequestListener:獲取客戶端的IP、訪問地址,訪問次數等。
3、SessionListener:需要監聽 Session 的建立與屬性變化。
4、程式碼如下:
- package com.helloweenvsfei.listener;
- import java.util.Date;
- import javax.servlet.ServletContextEvent;
- import javax.servlet.ServletContextListener;
- import com.helloweenvsfei.util.ApplicationConstants;
- publicclass MyContextListener implements ServletContextListener {
- publicvoid contextInitialized(ServletContextEvent event) {
- // 啟動時,記錄伺服器啟動時間
- ApplicationConstants.START_DATE = new Date();
- }
- publicvoid contextDestroyed(ServletContextEvent event) {
- // 關閉時,將結果清除。也可以將結果儲存到硬碟上。
- ApplicationConstants.START_DATE = null;
- ApplicationConstants.MAX_ONLINE_COUNT_DATE = null;
- }
- }
- package com.helloweenvsfei.listener;
- import javax.servlet.ServletRequestEvent;
- import javax.servlet.ServletRequestListener;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpSession;
- publicclass MyRequestListener implements ServletRequestListener {
- publicvoid requestDestroyed(ServletRequestEvent event) {
- }
- publicvoid requestInitialized(ServletRequestEvent event) {
- HttpServletRequest request = (HttpServletRequest) event
- .getServletRequest();
- HttpSession session = request.getSession(true);
- // 記錄IP地址
- session.setAttribute("ip", request.getRemoteAddr());
- // 記錄訪問次數,只記錄訪問 .html, .do, .jsp, .action 的累計次數
- String uri = request.getRequestURI();
- String[] suffix = { ".html", ".do", ".jsp", ".action" };
- for (int i=0; i<suffix.length; i++) {
- if (uri.endsWith(suffix[i])) {
- break;
- }
- if(i == suffix.length-1)
- return;
- }
- Integer activeTimes = (Integer) session.getAttribute("activeTimes");
- if (activeTimes == null) {
- activeTimes = 0;
- }
- session.setAttribute("activeTimes", activeTimes + 1);
- }
- }
- package com.helloweenvsfei.listener;
- import java.util.Date;
- import javax.servlet.http.HttpSession;
- import javax.servlet.http.HttpSessionAttributeListener;
- import javax.servlet.http.HttpSessionBindingEvent;
- import javax.servlet.http.HttpSessionEvent;
- import javax.servlet.http.HttpSessionListener;
- import com.helloweenvsfei.util.ApplicationConstants;
- publicclass MySessionListener implements HttpSessionListener,
- HttpSessionAttributeListener {
- publicvoid sessionCreated(HttpSessionEvent sessionEvent) {
- HttpSession session = sessionEvent.getSession();
- // 將 session 放入 map
- ApplicationConstants.SESSION_MAP.put(session.getId(), session);
- // 總訪問人數++
- ApplicationConstants.TOTAL_HISTORY_COUNT++;
- // 如果當前線上人數超過歷史記錄,則更新最大線上人數,並記錄時間
- if (ApplicationConstants.SESSION_MAP.size() > ApplicationConstants.MAX_ONLINE_COUNT) {
- ApplicationConstants.MAX_ONLINE_COUNT = ApplicationConstants.SESSION_MAP
- .size();
- ApplicationConstants.MAX_ONLINE_COUNT_DATE = new Date();
- }
- }
- publicvoid sessionDestroyed(HttpSessionEvent sessionEvent) {
- HttpSession session = sessionEvent.getSession();
- // 將session從map中移除
- ApplicationConstants.SESSION_MAP.remove(session.getId());
- }
- publicvoid attributeAdded(HttpSessionBindingEvent event) {
- if (event.getName().equals("personInfo")) {
- // 當前登入使用者數++
- ApplicationConstants.CURRENT_LOGIN_COUNT++;
- HttpSession session = event.getSession();
- // 查詢該帳號有沒有在其他機器上登入
- for (HttpSession sess : ApplicationConstants.SESSION_MAP.values()) {
- // 如果該帳號已經在其他機器上登入,則以前的登入失效
- if (event.getValue().equals(sess.getAttribute("personInfo"))
- && session.getId() != sess.getId()) {
- sess.invalidate();
- }
- }
- }
- }
- publicvoid attributeRemoved(HttpSessionBindingEvent event) {
- // 登出 當前登入使用者數--
- if (event.getName().equals("personInfo")) {
- ApplicationConstants.CURRENT_LOGIN_COUNT--;
- }
- }
- publicvoid attributeReplaced(HttpSessionBindingEvent event) {
- // 重新登入
- if (event.getName().equals("personInfo")) {
- HttpSession session = event.getSession();
- for (HttpSession sess : ApplicationConstants.SESSION_MAP.values()) {
- // 如果新帳號在其他機器上登入過,則以前登入失效
- if (event.getValue().equals(sess.getAttribute("personInfo"))
- && session.getId() != sess.getId()) {
- sess.invalidate();
- }
- }
- }
- }
- }