javaweb開發之Listener

1、 監聽器

WEB應用開發中的監聽器是指對整個WEB環境的監聽,當被監視的對象(ServletContext)發生狀況(生命週期,setAttribute)時,當即調用相應的方法進行處理。 html

實現了監聽者模式(觀察者模式)。 java

servlet 規範中爲每種事件監聽器都定義了相應的接口,在編寫事件監聽器程序時只需實現這些接口就能夠了。 web 服務器根據用戶編寫的事件監聽器所實現的接口把它註冊到相應的被監聽對象上。 web

一些Servlet事件監聽器須要在web應用程序的部署文件描述符文件(web.xml)中進行註冊,一個web.xml能夠註冊多個servlet事件監聽器。web服務器按照它們在web.xml中註冊順序來加載和註冊這些servlet事件監聽器。 瀏覽器

servlet事件監聽器的註冊和調用過程都是由web容器自動完成的,當發生被監聽的對象被建立,修改,銷燬等事件時,web容器將調用與之相關的servlet事件監聽器對象的相應方法,用戶在這些方法中編寫的事件處理代碼即被執行。 安全

因爲在一個web應用程序中只會爲每一個事件監聽器類建立一個實例對象,有可能出現多個線程同時調用一個事件監聽對象的狀況,因此要注意多線程安全問題。 服務器

2、Servlet 監聽器的分類

1. 按監聽的對象劃分

用於監聽應用程序環境對象(ServletContext)的事件監聽器 session

用於監聽用戶會話對象(HttpSession)的事件監聽器 多線程

用於監聽請求消息對象(ServletRequest)的事件監聽器 app

2. 按監聽的事件類項劃分

用於監聽域對象自身的建立和銷燬的事件監聽器 jsp

用於監聽域對象中的屬性的增長和刪除的事件監聽器

用於監聽綁定到HttpSession域中的某個對象的狀態的事件監聽器

3、Servlet 事件監聽器

1. 監聽域對象的建立和銷燬

域對象建立和銷燬的事件監聽器就是用來監聽 ServletContext, HttpSession, HttpServletRequest 這三個對象的建立和銷燬事件的監聽器。

1.1 域對象的建立和銷燬時機

ServletContext 

建立:是在 Web 服務器啓動並加載某個 Web 應用程序時建立相應的ServletContext 對象;

銷燬:是在 Web 服務器關閉或卸載時爲每一個 Web 應用程序銷燬相應的ServletContext 對象。


HttpSession 

建立:是在瀏覽器開始與服務器會話時建立;

銷燬:是在調用HttpSession.invalidate();超過了Session的最大有效時間間隔,服務器進程被中止的時候。


ServletRequest 

建立:每次請求開始時建立;

銷燬:每次訪問結束後銷燬 。


1.2 域對象的建立和銷燬監聽器

ServletContextListener 接口

ServletContextListener 接口用於監聽 ServletContext 對象的建立和銷燬事件。
當 ServletContext 對象被建立時,激發public void contextInitalized(ServletContextEvent sce)方法;
當 ServletContext 對象被銷燬時,激發public void contextDestroyed(ServletContextEvent sce)方法。

方法參數是ServletContextEvent事件是在servlet對象建立時自動激活的事件。


HttpSessionListener 接口

HttpSessionListener 接口用於監聽HttpSession對象的建立和銷燬。
建立一個Session時,激發public void sessionCreated(HttpSessionEvent se)方法;

銷燬一個Session時,激發public void sessionDestroyed(HttpSessionEvent se)方法。


ServletRequestListener接口 

ServletRequestListener 接口用於監聽ServletRequest 對象的建立和銷燬。
建立一個ServletRequest 對象時,激發public void requestInitialized(ServletRequestEvent sre)方法;

銷燬一個ServletRequest時,激發public void requestDestroyed(ServletRequestEvent sre)方法。


2. 監聽域對象中的屬性的增長和刪除

域對象中屬性的變動的事件監聽器就是用來監聽 ServletContext, HttpSession, HttpServletRequest 這三個對象中的屬性變動信息事件的監聽器。

這三個監聽器接口分別是ServletContextAttributeListener, HttpSessionAttributeListener 和ServletRequestAttributeListener,這三個接口中都定義了三個方法來處理被監聽對象中的屬性的增長,刪除和替換的事件,同一個事件在這三個接口中對應的方法名稱徹底相同,只是接受的參數類型不一樣。

2.1 attributeAdded() 方法

當向被監聽器對象中增長一個屬性時,web容器就調用事件監聽器的 attributeAdded 方法進行響應,這個方法接受一個事件類型的參數,監聽器能夠經過這個參數來得到正在增長屬性的域對象和被保存到域中的屬性對象。各個域屬性監聽器中的完整語法定義爲:

public void attributeAdded(ServletContextAttributeEvent scae) 
public void attributeAdded (HttpSessionBindingEvent  hsbe) 
public void attributeAdded (ServletRequestAttributeEvent srae)

2.2 attributeRemoved() 方法

當刪除被監聽對象中的一個屬性時,web 容器調用事件監聽器的這個方法進行響應。各個域屬性監聽器中的完整語法定義爲:

public void attributeRemoved(ServletContextAttributeEvent scae) 
public void attributeRemoved (HttpSessionBindingEvent  hsbe) 

public void attributeRemoved (ServletRequestAttributeEvent srae)

2.3 attributeReplaced() 方法

當監聽器的域對象中的某個屬性被替換時,web容器調用事件監聽器的這個方法進行相應。各個域屬性監聽器中的完整語法定義爲:
public void attributeReplaced(ServletContextAttributeEvent scae) 
public void attributeReplaced (HttpSessionBindingEvent  hsbe) 
public void attributeReplaced (ServletRequestAttributeEvent srae)

3. 監聽綁定到HttpSession域中的某個對象的狀態

保存在 Session 域中的對象能夠有多種狀態:綁定到  Session 中;從 Session 域中解除綁定;隨 Session 對象持久化到一個存儲設備中;隨 Session 對象從一個存儲設備中恢復。
Servlet 規範中定義了兩個特殊的監聽器接口來幫助 JavaBean 對象瞭解本身在 Session 域中的這些狀態:
          HttpSessionBindingListener接口
          HttpSessionActivationListener接口  
實現這兩個接口的類不須要 web.xml 文件中進行註冊。

3.1 HttpSessionBindingListener接口

實現了HttpSessionBindingListener接口的 JavaBean 對象能夠感知本身被綁定到 Session 中和從 Session 中刪除的事件 。
當對象被綁定到 HttpSession 對象中時,web 服務器調用該對象的  void valueBound(HttpSessionBindingEvent event) 方法 ;

當對象從 HttpSession 對象中解除綁定時,web 服務器調用該對象的 void valueUnbound(HttpSessionBindingEvent event)方法。

3.2 HttpSessionActivationListener接口

實現了HttpSessionActivationListener接口的 JavaBean 對象能夠感知本身被活化和鈍化的事件 。
當綁定到 HttpSession 對象中的對象將要隨 HttpSession 對象被鈍化以前,web 服務器調用該對象的  void sessionWillPassivate(HttpSessionBindingEvent event) 方法 ;
當綁定到 HttpSession 對象中的對象將要隨 HttpSession 對象被活化以後,web 服務器調用該對象的 void sessionDidActive(HttpSessionBindingEvent event)方法 。

4、應用

統計當前在線人數


User.java

package cn.heimar.online;

public class User {
	private String id;
	private String name;
	private String ip;

	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 String getIp() {
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}
}
IndexServlet.java
package cn.heimar.online;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class IndexServlet extends HttpServlet {

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		HttpSession session = req.getSession();
		User u = (User) session.getAttribute("USER_IN_SESSION");
		if (u == null) {
			u = new User();
			u.setId(session.getId());
			u.setIp(req.getRemoteAddr());
			session.setAttribute("USER_IN_SESSION", u);
		}
		req.getRequestDispatcher("/WEB-INF/login.jsp").forward(req, resp);
	}
}

LoginServlet.java

package cn.heimar.online;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class LoginServlet extends HttpServlet {
	private boolean hasLength(String s) {
		return s != null && !"".equals(s.trim());
	}

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		req.setCharacterEncoding("UTF-8");
		String name = req.getParameter("username");
		String password = req.getParameter("password");
		if (hasLength(name) && hasLength(password)) {
			HttpSession session = req.getSession();
			User u = (User) session.getAttribute("USER_IN_SESSION");
			u.setName(name);
			req.getRequestDispatcher("/WEB-INF/success.jsp").forward(req, resp);
		} else {
			resp.sendRedirect(req.getContextPath()+"/index");
		}
	}
}

OnlineServlet.java

package cn.heimar.online;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class OnlineServlet extends HttpServlet {

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		String cmd = req.getParameter("cmd");
		String id = req.getParameter("id");

		List<User> guests = new ArrayList<User>();
		List<User> users = new ArrayList<User>();

		@SuppressWarnings("unchecked")
		List<User> onlines = (List<User>) this.getServletContext()
				.getAttribute("ONLINE_IN_CTX");

		if ("remove".equals(cmd) && id != null && !"".equals(id)) {
			for (User u : onlines) {
				if (u.getId().equals(id)) {
					u.setName(null);
				}
			}
		}

		for (User u : onlines) {
			if (u.getName() != null) {
				users.add(u);
			} else {
				guests.add(u);
			}
		}

		req.setAttribute("guests", guests);
		req.setAttribute("users", users);

		req.getRequestDispatcher("/WEB-INF/online.jsp").forward(req, resp);
	}

}

OnlineServletCtxListener.java

package cn.heimar.online;

import java.util.ArrayList;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * 在線用戶統計ServletContext監聽器
 * 在應用啓動時,初始化在線用戶列表屬性「ONLINE_IN_CTX」
 */
public class OnlineServletCtxListener implements ServletContextListener {

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		sce.getServletContext().setAttribute("ONLINE_IN_CTX",
				new ArrayList<User>());
	}

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		// TODO Auto-generated method stub
	}

}

OnlineSessionAttrListener.java

package cn.heimar.online;

import java.util.List;

import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

/**
 * 在線用戶統計HttpSessionAttribute監聽器
 * 在當前用戶session中添加「USER_IN_SESSION」時,將該用戶添加到在線用戶列表
 */
public class OnlineSessionAttrListener implements HttpSessionAttributeListener {

	@Override
	public void attributeAdded(HttpSessionBindingEvent event) {
		String name = event.getName();
		if ("USER_IN_SESSION".equals(name)) {
			User u = (User) event.getValue();
			List<User> onlines = (List<User>) event.getSession()
					.getServletContext().getAttribute("ONLINE_IN_CTX");
			onlines.add(u);
		}
	}

	@Override
	public void attributeRemoved(HttpSessionBindingEvent se) {
		// 能夠在用戶退出後將當前用戶從在線用戶列表中刪除。此處不演示
	}

	@Override
	public void attributeReplaced(HttpSessionBindingEvent se) {
		// TODO Auto-generated method stub
	}

}

OnlineSessionListener.java

package cn.heimar.online;

import java.util.List;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
 * 在線用戶統計HttpSession監聽器
 * 在當前用戶session銷燬時,將該用戶從在線用戶列表中移除
 */
public class OnlineSessionListener implements HttpSessionListener {

	@Override
	public void sessionCreated(HttpSessionEvent se) {
		// TODO Auto-generated method stub
	}

	@Override
	public void sessionDestroyed(HttpSessionEvent se) {
		User u = (User) se.getSession().getAttribute("USER_IN_SESSION");
		List<User> onlines = (List<User>) se.getSession().getServletContext()
				.getAttribute("ONLINE_IN_CTX");
		for (int i = 0; i < onlines.size(); i++) {
			User t = onlines.get(i);
			if (t.getId().equals(u.getId())) {
				onlines.remove(i);
			}
		}
	}

}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">
	<!--註冊監聽器-->
	<listener>
		<listener-class>cn.heimar.online.OnlineServletCtxListener</listener-class>
	</listener>
	<listener>
		<listener-class>cn.heimar.online.OnlineSessionAttrListener</listener-class>
	</listener>
	<listener>
		<listener-class>cn.heimar.online.OnlineSessionListener</listener-class>
	</listener>

	<servlet>
		<servlet-name>index</servlet-name>
		<servlet-class>cn.heimar.online.IndexServlet</servlet-class>
	</servlet>
	<servlet>
		<servlet-name>login</servlet-name>
		<servlet-class>cn.heimar.online.LoginServlet</servlet-class>
	</servlet>
	<servlet>
		<servlet-name>online</servlet-name>
		<servlet-class>cn.heimar.online.OnlineServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>login</servlet-name>
		<url-pattern>/login</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>index</servlet-name>
		<url-pattern>/index</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>online</servlet-name>
		<url-pattern>/online</url-pattern>
	</servlet-mapping>
</web-app>

login.jsp

<%@ 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>
<form method="post" action="<%=request.getContextPath() %>/login">
<table>
	<tr>
		<td>姓名:</td>
		<td><input type="text" name="username" /></td>
	</tr>
	<tr>
		<td>密碼:</td>
		<td><input type="password" name="password" /></td>
	</tr>
	<tr>
		<td colspan="2"><input type="submit" value="登錄" /></td>
	</tr>
</table>
</form>
</body>
</html>
online.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 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>
遊客:
<table border="1">
	<tr>
		<td width="100">ID</td>
		<td width="100">IP</td>
	</tr>
	<c:forEach var="g" items="${guests }">
		<tr>
			<td>${g.id }</td>
			<td>${g.ip }</td>
		</tr>
	</c:forEach>
</table>
<br/>
<br/>
在線用戶:
<table border="1">
	<tr>
		<td width="100">ID</td>
		<td width="100">用戶名</td>
		<td width="100">操做</td>
	</tr>
	<c:forEach var="u" items="${users }">
		<tr>
			<td>${u.id }</td>
			<td>${u.name }</td>
			<td><a href="<%=request.getContextPath() %>/online?cmd=remove&id=${u.id }">T掉</a></td>
		</tr>
	</c:forEach>
</table>
</body>
</html>
success.jsp
<%@ 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>
登錄成功..
<br/>
<a href="<%=request.getContextPath() %>/online">查看在線用戶列表</a>
</body>
</html>
相關文章
相關標籤/搜索