WEB应用开发中的监听器是指对整个WEB环境的监听,当被监视的对象(ServletContext)发生情况(生命周期,setAttribute)时,立即调用相应的方法进行处理。
实现了监听者模式(观察者模式)。
servlet 规范中为每种事件监听器都定义了相应的接口,在编写事件监听器程序时只需实现这些接口就可以了。 web 服务器根据用户编写的事件监听器所实现的接口把它注册到相应的被监听对象上。
一些Servlet事件监听器需要在web应用程序的部署文件描述符文件(web.xml)中进行注册,一个web.xml可以注册多个servlet事件监听器。web服务器按照它们在web.xml中注册顺序来加载和注册这些servlet事件监听器。
servlet事件监听器的注册和调用过程都是由web容器自动完成的,当发生被监听的对象被创建,修改,销毁等事件时,web容器将调用与之相关的servlet事件监听器对象的相应方法,用户在这些方法中编写的事件处理代码即被执行。
由于在一个web应用程序中只会为每个事件监听器类创建一个实例对象,有可能出现多个线程同时调用一个事件监听对象的情况,所以要注意多线程安全问题。
用于监听应用程序环境对象(ServletContext)的事件监听器
用于监听用户会话对象(HttpSession)的事件监听器
用于监听请求消息对象(ServletRequest)的事件监听器
用于监听域对象自身的创建和销毁的事件监听器
用于监听域对象中的属性的增加和删除的事件监听器
用于监听绑定到HttpSession域中的某个对象的状态的事件监听器域对象创建和销毁的事件监听器就是用来监听 ServletContext, HttpSession, HttpServletRequest 这三个对象的创建和销毁事件的监听器。
ServletContext
创建:是在 Web 服务器启动并加载某个 Web 应用程序时创建相应的ServletContext 对象;销毁:是在 Web 服务器关闭或卸载时为每个 Web 应用程序销毁相应的ServletContext 对象。
HttpSession
创建:是在浏览器开始与服务器会话时创建;销毁:是在调用HttpSession.invalidate();超过了Session的最大有效时间间隔,服务器进程被停止的时候。
ServletRequest
创建:每次请求开始时创建;销毁:每次访问结束后销毁 。
ServletContextListener 接口
ServletContextListener 接口用于监听 ServletContext 对象的创建和销毁事件。方法参数是ServletContextEvent事件是在servlet对象创建时自动激活的事件。
HttpSessionListener 接口
HttpSessionListener 接口用于监听HttpSession对象的创建和销毁。销毁一个Session时,激发public void sessionDestroyed(HttpSessionEvent se)方法。
ServletRequestListener接口
ServletRequestListener 接口用于监听ServletRequest 对象的创建和销毁。销毁一个ServletRequest时,激发public void requestDestroyed(ServletRequestEvent sre)方法。
域对象中属性的变更的事件监听器就是用来监听 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 (ServletRequestAttributeEvent srae)
当对象从 HttpSession 对象中解除绑定时,web 服务器调用该对象的 void valueUnbound(HttpSessionBindingEvent event)方法。
统计当前在线人数
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>