JavaWeb(六)Listener監聽器
監聽器
監聽器就是一個實現特定介面的普通java程式,這個程式專門用於監聽另一個java物件的方法呼叫或屬性改變,當被監聽物件發生上述事件後,監聽器某個方法將立即被執行。
Servlet監聽器
在Servlet規範中定義了多種型別的監聽器,它們用於監聽的事件源分別為 ServletContext, HttpSession 和 ServletRequest 這三個域物件。
Servlet規範針對這三個物件上的操作,又把這多種型別的監聽器劃分為三種類型。
- 監聽三個域物件建立和銷燬的事件監聽器
- 監聽域物件中屬性的增加和刪除的事件監聽器
- 監聽繫結到 HttpSession 域中的某個物件的狀態的事件監聽器。
編寫監聽器
和編寫其它事件監聽器一樣,編寫servlet監聽器也需要實現一個特定的介面,並針對相應動作覆蓋介面中的相應方法。
和其它事件監聽器略有不同的是,servlet監聽器的註冊不是直接註冊在事件源上,而是由WEB容器負責註冊,開發人員只需在web.xml檔案中使用<listener>標籤配置好監聽器,web容器就會自動把監聽器註冊到事件源中。
一個 web.xml 檔案中可以配置多個 Servlet 事件監聽器,web 伺服器按照它們在 web.xml 檔案中的註冊順序來載入和註冊這些 Serlvet 事件監聽器。
ServletContext監聽器
ServletContextListener 介面用於監聽 ServletContext 物件的建立和銷燬事件。
當 ServletContext 物件被建立時,激發contextInitialized (ServletContextEvent sce)方法
當 ServletContext 物件被銷燬時,激發contextDestroyed(ServletContextEvent sce)方法。
servletContext域物件何時建立和銷燬:
- 建立:伺服器啟動針對每一個web應用建立servletcontext
- 銷燬:伺服器關閉前先關閉代表每一個web應用的servletContext
1 package com.hanqi.listener; 2 3 import javax.servlet.ServletContextEvent; 4 import javax.servlet.ServletContextListener; 5 6 public class TestApplicationListener implements ServletContextListener { 7 8 @Override 9 public void contextDestroyed(ServletContextEvent arg0) { 10 System.out.println("application物件銷燬"); 11 } 12 13 @Override 14 public void contextInitialized(ServletContextEvent arg0) { 15 System.out.println("建立application物件"); 16 } 17 }
訪問時會在控制檯列印“建立application物件”
HttpSession監聽器
HttpSessionListener介面用於監聽HttpSession的建立和銷燬
建立一個Session時,sessionCreated(HttpSessionEvent se) 方法將會被呼叫。
銷燬一個Session時,sessionDestroyed (HttpSessionEvent se) 方法將會被呼叫。
Session域物件建立和銷燬的時機建立:使用者每一次訪問時,伺服器建立session
- 銷燬:如果使用者的session 30分鐘沒有使用,伺服器就會銷燬session,我們在web.xml裡面也可以配置session失效時間
ServletRequest 監聽器
ServletRequestListener 介面用於監聽ServletRequest 物件的建立和銷燬。
Request 物件被建立時,監聽器的requestInitialized方法將會被呼叫。
Request物件被銷燬時,監聽器的requestDestroyed方法將會被呼叫。
servletRequest域物件建立和銷燬的時機:
- 建立:使用者每一次訪問,都會建立一個reqeust
- 銷燬:當前訪問結束,request物件就會銷燬
當向被監聽器物件中增加一個屬性時,web容器就呼叫事件監聽器的 attributeAdded 方法進行相應,這個方法接受一個事件型別的引數,監聽器可以通過這個引數來獲得正在增加屬性的域物件和被儲存到域中的屬性物件
各個域屬性監聽器中的完整語法定義為:
- public void attributeAdded(ServletContextAttributeEvent scae)
- public void attributeAdded (HttpSessionBindingEvent hsbe)
- public void attributeAdded(ServletRequestAttributeEvent srae)
監聽三個域的屬性變化
Servlet規範定義了監聽 ServletContext, HttpSession, HttpServletRequest 這三個物件中的屬性變更資訊事件的監聽器。
這三個監聽器介面分別是ServletContextAttributeListener, HttpSessionAttributeListener ServletRequestAttributeListener
這三個介面中都定義了三個方法來處理被監聽物件中的屬性的增加,刪除和替換的事件,同一個事件在這三個介面中對應的方法名稱完全相同,只是接受的引數型別不同。
當向被監聽器物件中增加一個屬性時,web容器就呼叫事件監聽器的 attributeAdded 方法進行相應,這個方法接受一個事件型別的引數,監聽器可以通過這個引數來獲得正在增加屬性的域物件和被儲存到域中的屬性物件
各個域屬性監聽器中的完整語法定義為:
- public void attributeAdded(ServletContextAttributeEvent scae)
- public void attributeAdded (HttpSessionBindingEvent hsbe)
- public void attributeAdded(ServletRequestAttributeEvent srae)
當刪除被監聽物件中的一個屬性時,web 容器呼叫事件監聽器的這個方法進行相應
各個域屬性監聽器中的完整語法定義為:
- public void attributeRemoved(ServletContextAttributeEvent scae)
- public void attributeRemoved (HttpSessionBindingEvent hsbe)
- public void attributeRemoved (ServletRequestAttributeEvent srae)
當監聽器的域物件中的某個屬性被替換時,web容器呼叫事件監聽器的這個方法進行相應
各個域屬性監聽器中的完整語法定義為:
- public void attributeReplaced(ServletContextAttributeEvent scae)
- public void attributeReplaced (HttpSessionBindingEvent hsbe)
- public void attributeReplaced (ServletRequestAttributeEvent srae)
例子:
1 package com.hanqi.listener.attr;
2
3 import javax.servlet.ServletRequestAttributeEvent;
4 import javax.servlet.ServletRequestAttributeListener;
5
6 public class TestRequestAttributeListener implements ServletRequestAttributeListener {
7
8 @Override
9 public void attributeAdded(ServletRequestAttributeEvent arg0) {
10 System.out.println("request物件新增屬性");
11 }
12
13 @Override
14 public void attributeRemoved(ServletRequestAttributeEvent arg0) {
15 System.out.println("移除request屬性");
16 }
17
18 @Override
19 public void attributeReplaced(ServletRequestAttributeEvent arg0) {
20 System.out.println("替換request屬性");
21 }
22
23 }
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
request.setAttribute("hanqi", "淄博漢企");//設定request屬性
request.removeAttribute("hanqi");移除request屬性
session.getCreationTime();//獲取session的建立時間
%>
<h1>首頁</h1>
<hr>
當前線上人數: <%=application.getAttribute("userCount") %>
<hr>
使用者列表:
User [sessionid, session建立時間]
</body>
</html>
應用:統計線上使用者數量和使用者資訊
1 package com.util;
2
3 import java.util.Date;
4
5 public class User {
6 private String sid;
7 private Date stime;
8
9 public String getSid() {
10 return sid;
11 }
12 public void setSid(String sid) {
13 this.sid = sid;
14 }
15 public Date getStime() {
16 return stime;
17 }
18 public void setStime(Date stime) {
19 this.stime = stime;
20 }
21 @Override
22 public String toString() {
23 return "User :sid=" + sid + ", stime=" + stime + "|";
24 }
25 }
1 package com.listener;
2
3 import java.text.SimpleDateFormat;
4 import java.util.ArrayList;
5 import java.util.Date;
6 import java.util.Iterator;
7
8 import javax.servlet.annotation.WebListener;
9 import javax.servlet.http.HttpSession;
10 import javax.servlet.http.HttpSessionEvent;
11 import javax.servlet.http.HttpSessionListener;
12
13 import com.util.User;
14
15 /**
16 * Application Lifecycle Listener implementation class SessionListener
17 *
18 */
19 @WebListener
20 public class SessionListener implements HttpSessionListener {
21
22 private int userCount;//記錄線上人數
23 private ArrayList<User> userList;//記錄線上人的資訊
24 /**
25 * Default constructor.
26 */
27 public SessionListener() {
28 // TODO Auto-generated constructor stub
29 }
30
31 /**
32 * @see HttpSessionListener#sessionCreated(HttpSessionEvent)
33 */
34 //session建立時的方法
35 public void sessionCreated(HttpSessionEvent sessionEvent) {
36 // TODO Auto-generated method stub
37 userCount++;//使用者每一次訪問時,伺服器建立session,使用者數量+1
38 if(userList==null) {
39 userList = new ArrayList<User>();
40 }
41 //獲取HttpSession,便於顯示,不用每一次都呼叫
42 HttpSession session = sessionEvent.getSession();
43
44 String sessionid = session.getId();//獲取唯一ID
45 long stime = session.getCreationTime();//獲取建立時間
46 //呼叫方法檢視是不是已經存在的使用者,如果不存在,新建使用者存入資訊,加到集合中
47 if(!checkUsersSession(sessionid, userList)) {
48 User u = new User();
49 u.setStime(new Date(stime));
50 u.setSid(sessionid);
51 userList.add(u);
52 }
53 //設定session屬性,線上人數和線上資訊
54 session.getServletContext().setAttribute("userCount", userCount);
55 session.getServletContext().setAttribute("userList", userList);
56 }
57
58 private boolean checkUsersSession(String sessionid, ArrayList<User> userList2) {
59 //檢視是不是已經存在的使用者,遍歷集合,如果集合中有某個元素的id等於傳入的id,返回true
60 for(User u : userList2) {
61 if(u.getSid().equals(sessionid)) {
62 return true;
63 }
64 }
65 return false;
66 }
67
68 /**
69 * @see HttpSessionListener#sessionDestroyed(HttpSessionEvent)
70 */
71 //session銷燬時的方法
72 public void sessionDestroyed(HttpSessionEvent sessionEvent) {
73 //使用者下線,人數-1
74 userCount--;
75 HttpSession session = sessionEvent.getSession();
76
77 String sessionid = session.getId();
78 System.out.println(sessionid + "的使用者下線");
79 //獲取銷燬的使用者的sessionid,在從集合中找到,移除
80 if(checkUsersSession(sessionid, userList)) {
81 Iterator<User> iter = userList.iterator();
82 while(iter.hasNext()) {
83 User u = iter.next();
84 if(u.getSid().equals(sessionid)) {
85 iter.remove();
86 }
87 }
88 }
89 session.getServletContext().setAttribute("userCount", userCount);
90 session.getServletContext().setAttribute("userList", userList);
91 }
92 }
1 <%@ page language="java" contentType="text/html; charset=utf-8"
2 pageEncoding="utf-8" import="java.util.ArrayList,com.util.User"%>
3 <%@ page import="java.util.*" %>
4 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
5 <html>
6 <head>
7 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
8 <title>Insert title here</title>
9 </head>
10 <body>
11
12 當前線上人數: <%=application.getAttribute("userCount") %>
13 <hr>
14 線上使用者列表:<br>
15 <%
16 List<User> userList = (List<User>)application.getAttribute("userList");//獲取application物件,使用者資訊集合
17 if(userList!=null) {//如果集合不為空,遍歷顯示
18 for(User u : userList) {
19 out.print(u+"<br>");
20 }
21 }
22 %>
23 </body>
24 </html>
嘗試加一個踢人推出的功能