1. 監聽器概述
1.1. 什么是監聽器
做過Swing或者AWT圖像界面Java程序開發的話,應該對Listener與Event非常熟悉。Swing或者AWT中通過Listener與Event來處理事件,如鼠標事件、鍵盤事件等。先實現一個Listener接口,然后通過addListener()方法把Listener綁定到某個控件上,單機按鈕時就會調用Listener的相應方法,並傳回一個Event對象。
Java Web程序也一樣,使用Listener與Event完成相應事件的處理。使用Listener不需要關注該類事件是怎樣觸發的或者怎么調用相應的Listener,只要記住該類事件觸發時一定會調用相應的Listener。遵循Servlet規范的服務器完成了相應的工作。開發者只要在Listener里編寫相關代碼就OK了。
1.2. 八大監聽器
Java Web一共提供了八個監聽器供使用,分別用於監聽Request、Session和ServletContext等的創建與銷毀、屬性變化。這八個監聽器分別如下:
- ServletRequestListner
| Method Summary |
|
| void |
requestDestroyed(ServletRequestEvent sre) |
| void |
requestInitialized(ServletRequestEvent sre) |
- ServletRequestAttributeListener
| Method Summary |
|
| void |
attributeAdded(ServletRequestAttributeEvent srae) |
| void |
attributeRemoved(ServletRequestAttributeEvent srae) |
| void |
attributeReplaced(ServletRequestAttributeEvent srae) |
- HttpSessionListener
| Method Summary |
|
| void |
sessionCreated(HttpSessionEvent se) |
| void |
sessionDestroyed(HttpSessionEvent se) |
- HttpSessionAttributeListener
| Method Summary |
|
| void |
attributeAdded(HttpSessionBindingEvent se) |
| void |
attributeRemoved(HttpSessionBindingEvent se) |
| void |
attributeReplaced(HttpSessionBindingEvent se) |
- ServletContextListener
| Method Summary |
|
| void |
contextDestroyed(ServletContextEvent sce) |
| void |
contextInitialized(ServletContextEvent sce) |
- ServletContextAttributeListener
| Method Summary |
|
| void |
attributeAdded(ServletContextAttributeEvent scab) |
| void |
attributeRemoved(ServletContextAttributeEvent scab) |
| void |
attributeReplaced(ServletContextAttributeEvent scab) |
- HttpSessionBindingListener
| Method Summary |
|
| void |
valueBound(HttpSessionBindingEvent event) |
| void |
valueUnbound(HttpSessionBindingEvent event) |
- HttpSessionActivationListener
| Method Summary |
|
| void |
sessionDidActivate(HttpSessionEvent se) |
| void |
sessionWillPassivate(HttpSessionEvent se) |
1.3. 自定義監聽器
如果要自定義一個監聽器的話,需要如下幾步:
- 創建一個自定義監聽器類,並實現上述八個監聽器中的一個或多個。
- 配置Web工程的web.xml文件,格式如下:
<listener> <listener-class>自定義監聽器的完整路徑</listener-class> </listener>
2. 創建銷毀監聽器
2.1. ServletRequestListener
ServletRequestListener用於監聽Request的創建與銷毀。用戶每次請求Request都會執行requestInitialized()方法,Request處理完畢自動銷毀前執行requestDestroyed()方法。
需要注意的是如果一個頁面內含有多個圖片等元素,則請求一次頁面可能會觸發多次Request事件。
2.2. HttpSessionListener
HttpSessionListener用於監聽Session的創建與銷毀。創建Session時執行sessionCreated()方法,銷毀Session或Session超時時執行sessionDestroyed()方法。該Listener可用於收集在線者信息。
2.3. ServletContextListener
ServletContextListener用於監聽ServletContext的創建與銷毀。服務器啟動或者部署Web應用程序時執行contextInitialized()方法,服務器關閉或關閉Web應用程序時執行contextDestroyed()方法。該Listener可用於啟動時獲取web.xml文件中配置的初始化參數。
2.4. 監聽Request、Session及ServletContext
編寫一個監聽器,分別實現ServletRequestListener、HttpSessionListener和ServletContextListener,並重寫上述三個接口提供的方法。
- 編寫一個自定義監聽器。
public class ListenerTest implements ServletRequestListener, HttpSessionListener, ServletContextListener { /** * requestInitialized()用於監聽Request對象的創建. * * 該方法在Request對象創建后被調用. */ public void requestInitialized(ServletRequestEvent sre) { System.out.println("客戶端向服務器端發送了一次請求......"); } /** * requestDestroyed()用於監聽Request對象的銷毀. * * 該方法在Request對象銷毀前被調用. */ public void requestDestroyed(ServletRequestEvent sre) { System.out.println("客戶端的請求結束了......"); } /** * sessionCreated()用於監聽Session對象的創建. * * 該方法在Session對象創建后被調用. */ public void sessionCreated(HttpSessionEvent se) { System.out.println("新創建了一個session......"); } /** * sessionDestroyed()用於監聽Session對象的銷毀. * * 該方法在Session對象銷毀前被調用. */ public void sessionDestroyed(HttpSessionEvent se) { System.out.println("銷毀了一個session......"); } /** * contextInitialized()用於監聽ServletContext對象的創建. * * 該方法在ServletContext對象創建后被調用. */ public void contextInitialized(ServletContextEvent sce) { System.out.println("Web應用即將啟動......"); } /** * contextDestroyed()用於監聽ServletContext對象的銷毀. * * 該方法在ServletContext對象銷毀前被調用. */ public void contextDestroyed(ServletContextEvent sce) { System.out.println("Web應用即將關閉......"); } }
- 在web.xml文件中配置自定義監聽器。
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <display-name></display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <listener> <listener-class>app.java.listener.ListenerTest</listener-class> </listener> </web-app>
3. 屬性操作監聽器
3.1. ServletRequestAttributeListener
ServletRequestAttributeListener用於監聽Request對象添加、更新和移除屬性。Request添加屬性時執行requestAdded()方法,Request對象更新屬性時執行requestReplaced()方法,Request對象移除屬性時執行requestRemoved()方法。
3.2. HttpSessionAttributeListener
HttpSessionAttributeListener用於監聽Session對象添加、更新和移除屬性。Session添加屬性時執行sessionAdded()方法,Session對象更新屬性時執行sessionReplaced()方法,Session對象移除屬性時執行sessionRemoved()方法。
3.3. ServletContextAttributeListener
ServletContextAttributeListener用於監聽ServletContext對象添加、更新和移除屬性。ServletContext添加屬性時執行contextAdded()方法,ServletContext對象更新屬性時執行contextReplaced()方法,Session對象移除屬性時執行contextRemoved()方法。
3.4. 監聽ServletRequestAttributeListener、HttpSessionAttributeListener及ServletContextAttributeListener
編寫一個監聽器,分別實現ServletRequestAttributeListener、HttpSessionAttributeListener和ServletContextAttributeListener,並重寫上述三個接口提供的方法。
- 編寫一個自定義監聽器。
public class AttributeListenerTest implements ServletRequestAttributeListener, HttpSessionAttributeListener, ServletContextAttributeListener { /** * attributeAdded(ServletRequestAttributeEvent srae)用於監聽Request對象添加屬性. * * 該方法在Request對象添加屬性后被調用. */ public void attributeAdded(ServletRequestAttributeEvent srae) { System.out.println("成功向Request對象添加了一個屬性......"); } /** * attributeRemoved(ServletRequestAttributeEvent srae)用於監聽Request對象刪除屬性. * * 該方法在Request對象刪除屬性前被調用. */ public void attributeRemoved(ServletRequestAttributeEvent srae) { System.out.println("成功向Request對象刪除了一個屬性......"); } /** * attributeReplaced(ServletRequestAttributeEvent srae)用於監聽Request對象更新屬性. * * 該方法在Request對象更新屬性前被調用. */ public void attributeReplaced(ServletRequestAttributeEvent srae) { System.out.println("成功向Request對象更新了一個屬性......"); } /** * attributeAdded(HttpSessionBindingEvent se)用於監聽Session對象添加屬性. * * 該方法在Session對象添加屬性后被調用. */ public void attributeAdded(HttpSessionBindingEvent se) { System.out.println("成功向Session對象添加了一個屬性......"); } /** * attributeRemoved(HttpSessionBindingEvent se)用於監聽Session對象刪除屬性. * * 該方法在Session對象刪除屬性前被調用. */ public void attributeRemoved(HttpSessionBindingEvent se) { System.out.println("成功向Session對象刪除了一個屬性......"); } /** * attributeReplaced(HttpSessionBindingEvent se)用於監聽Session對象更新屬性. * * 該方法在Session對象更新屬性前被調用. */ public void attributeReplaced(HttpSessionBindingEvent se) { System.out.println("成功向Session對象更新了一個屬性......"); } /** * attributeAdded(ServletContextAttributeEvent scab)用於監聽ServletContex對象添加屬性. * * 該方法在ServletContex對象添加屬性后被調用. */ public void attributeAdded(ServletContextAttributeEvent scab) { System.out.println("成功向ServletContex對象添加了一個屬性......"); } /** * attributeRemoved(ServletContextAttributeEvent scab)用於監聽ServletContex對象刪除屬性. * * 該方法在ServletContex對象刪除屬性前被調用. */ public void attributeRemoved(ServletContextAttributeEvent scab) { System.out.println("成功向ServletContex對象刪除了一個屬性......"); } /** * attributeReplaced(ServletContextAttributeEvent scab)用於監聽ServletContex對象更新屬性. * * 該方法在ServletContex對象更新屬性前被調用. */ public void attributeReplaced(ServletContextAttributeEvent scab) { System.out.println("成功向ServletContex對象更新了一個屬性......"); } }
- 在web.xml文件中配置自定義監聽器。
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <display-name></display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <listener> <listener-class>app.java.listener.AttributeListenerTest</listener-class> </listener> </web-app>
4. Session綁定監聽器
4.1. JavaBean感知監聽
HttpSessionBindingListener用於當JavaBean對象被放到Session里時執行valueBound()方法,當JavaBean對象從Session移除時執行valueUnBound()方法。JavaBean必須實現該Listener接口。
- 創建一個JavaBean,實現HttpSessionBindingListener接口。
public class User implements HttpSessionBindingListener { private String name; private int age; private String job; public User(String name, int age, String job) { this.name = name; this.age = age; this.job = job; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } @Override public String toString() { return "User [name=" + name + ", age=" + age + ", job=" + job + "]"; } /** * valueBound()將該JavaBean對象放置到Session時被調用. */ public void valueBound(HttpSessionBindingEvent event) { System.out.println("該JavaBean被添加到session中......"); } /** * valueUnbound()將該JavaBean對象從Session移除時被調用. */ public void valueUnbound(HttpSessionBindingEvent event) { System.out.println("該JavaBean從session中被移除......"); } }
- 創建一個Servlet用於將JavaBean對象放置到Session和從Session移除。
public class BeanServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); session.setAttribute("user", new User("zhangwuji", 18, "jiaozhu")); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); session.removeAttribute("user"); } }
使用HttpSessionBindingListener監聽器時,不需要配置web.xml文件。
5. Session鈍化與活化
5.1. Session序列化
所謂Session序列化就是指當Tomcat服務器關閉時Session會被保存到本地硬盤中,當Tomcat服務器啟動時Session會從本地硬盤讀取到內存中的過程。
- 創建一個Servlet用於向Session中存放數據內容。
public class SetSessionServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); session.setAttribute("name", "zhangwuji"); System.out.println("成功向session存儲了數據......"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
- 創建一個Servlet用於從Session中讀取存放的數據內容。
public class GetSessionServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); String name = (String) session.getAttribute("name"); System.out.println("session中存儲的數據內容為: "+name+"......"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
- 發布Web工程到Tomcat服務器,並啟動Tomcat服務器。訪問向Session存儲數據的Servlet。
- 關閉Tomcat服務器,查看${CATALINA_HOME}\work\Catalina\localhost\Web工程名目錄。
- 重新啟動Tomcat服務器,訪問從Session讀取存儲數據的Servlet。
當Tomcat服務器被重新啟動讀取Session內容之后,序列化到本地硬盤中的Session文件會自動銷毀。
如果想要關閉Session的序列化,可以修改Tomcat安裝目錄中conf目錄的context.xml文件。具體內容如下:
<Manager pathname=""/>
5.2. Session鈍化與活化
當Session長時間不被使用時(沒有超時),將Session里的內容保存到本地硬盤中,這個過程叫做鈍化。當Session重新被使用時,將保存到本地硬盤中的Session里的內容重新讀取,這個過程叫做活化。
- 創建一個Servlet用於向Session中存放數據內容。
public class SetSessionServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); session.setAttribute("name", "zhangwuji"); System.out.println("成功向session存儲了數據......"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
- 創建一個Servlet用於從Session中讀取存放的數據內容。
public class GetSessionServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); String name = (String) session.getAttribute("name"); System.out.println("session中存儲的數據內容為: "+name+"......"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }
- 修改Tomcat安裝目錄/conf目錄/ Catalina目錄/ localhost目錄中,創建一個名為Web工程名的xml文件。
<?xml version="1.0" encoding="UTF-8"?> <Context> <!-- maxIdleSwap:指定多長時間后Session會被鈍化.(單位為分鍾) --> <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1" > <!-- directory:指定鈍化文件的保存目錄. 鈍化文件保存路徑:${CATALINA_HOME}\work\Catalina\localhost\Web工程名\${sessionid}.session --> <Store className="org.apache.catalina.session.FileStore" directory="mysession" /> </Manager> </Context>
鈍化文件在重新被使用時,不會自動銷毀。
6. 監聽器案例
6.1. 統計在線人數
利用Java Web的監聽器技術,實現在線人數統計的案例,具體實現步驟如下:
- 創建一個監聽器,實現ServletContextListener接口,用於初始化在線人數為0。
public class OnLineCountServletContextListener implements ServletContextListener { public void contextInitialized(ServletContextEvent sce) { // 通過 事件對象,獲得事件源ServletContext ServletContext servletContext = sce.getServletContext(); // 只會執行一次 servletContext.setAttribute("onlinecount", 0); } public void contextDestroyed(ServletContextEvent sce) {} }
- 創建一個監聽器,實現HttpSessionListener接口,用於統計在線人數。
public class OnLineCountHttpSessionListener implements HttpSessionListener { public void sessionCreated(HttpSessionEvent se) { // 獲得ServletContext ServletContext servletContext = se.getSession().getServletContext(); // 獲得原來在線人數 int onlinecount = (Integer) servletContext.getAttribute("onlinecount"); System.out.println(se.getSession().getId() + "在線了..."); servletContext.setAttribute("onlinecount", onlinecount + 1); } public void sessionDestroyed(HttpSessionEvent se) { // 獲得ServletContext ServletContext servletContext = se.getSession().getServletContext(); // 獲得原來在線人數 int onlinecount = (Integer) servletContext.getAttribute("onlinecount"); System.out.println(se.getSession().getId() + "離線了..."); servletContext.setAttribute("onlinecount", onlinecount - 1); } }
- 配置Web工程的web.xml文件。
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <display-name></display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <listener> <listener-class>app.java.demo.OnLineCountServletContextListener</listener-class> </listener> <listener> <listener-class>app.java.demo.OnLineCountHttpSessionListener</listener-class> </listener> </web-app>
- 創建一個JSP頁面用於顯示在線人數。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>在線人數統計案例</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <h1>在線人數:${applicationScope.onlinecount }</h1> </body> </html>
- 發布Web工程到Tomcat服務器,並啟動Tomcat服務器。訪問對應的JSP頁面查看結果。
