本章要点:
- 理解监听器的作用
- 掌握监听器的开发与部署
- 编写在线人数统计
在Web应用中,有时候你可能想要在Web应用程序启动和关闭时来执行一些任务(如数据库连接的建立和释放),或者你想要监控 Session 的创建和销毁,你还希望在ServletContext、HttpSession, 以及ServletRequest 对象中的属性发生改变时得到通知,那么你可以通过Servlet 监听器来实现你的目的。
15.1 监听器接口
Servlet API 中定义了8个监听器接口,可以用于监听 ServletContext、HttpSession 和 ServletRequest 对象的生命周期事件,以及这些对象的属性改变事件。这8个 监听器接口如表15-1 所示。
15.1.1 监听器接口 列表
| 监听器接口 | 方法 | 说明 |
|---|---|---|
| ServletContextListener | ||
| javax.servlet.ServletContextListener | contextDestroyed contextInitialized |
如果想要在Servlet上下文对象(ServletContext)初始化时或者将要被销毁时得到通知,可以实现这个接口。实现该接口的类必须在Web应用程序的部署描述符中进行配置 |
| javax.servlet.ServletContext.AttributeListener | attributeAdded attributeRemoved attributeReplaced |
如果想在Servlet上下文中的属性列表发生改变时得到通知,可以实现这个接口。实现该接口的类必须在Web应用程序的部署描述符中进行配置 |
| HttpSessionListener | ||
| java.servlet.http.HttpSessionListener | sessionCreated sessionDestroyed |
如果想要在Session创建后 或者在Session无效前得到通知,可以实现这个接口。实现该接口的类必须在Web应用程序的部署描述符中进行配置 |
| javax.servlet.htttp.HttpSessionActivationListener | sessionDidActivate sessionWillPassivate |
实现这个接口的对象,如果绑定到Session中,当Session被钝化或激活时,Servlet容器将通知该对象 |
| javax.servlet.htttp.HttpSessionBindingListener | valueBound valueUnbound |
如果想让一个对象在绑定到Session中或者从Session中被删除时得到通知,那么可以让这个对象实现该接口 |
| javax.servlet.http.HttpSessionAttributeListener | attributeAdded attributeRemoved attributeReplaced |
如果想要在Session中的属性列表发生改变时得到通知,可以实现这个接口 |
| ServletRequestListener | ||
| javax.servlet.ServletRequestListener | requestDestroyed requestInitialized |
如果想要在请求对象初始化时或者将要被销毁时得到通知,可以实现这个接口 |
| javax.servlet.ServletRequestAttributeListener | attributeAdded attributeRemoved attributeReplaced |
如果想要在Servlet 请求对象中的属性发生改变时得到通知,可以实现这个接口 |
HttpSessionAttributeListener 和 HttpSessionBindingListener 接口的主要区别是:前者用于监听Session中何时添加、删除、或者替换了某种类型的属性,而后者是由属性自身来实现,以便属性知道它何时添加到一个Session中,或者何时从Session中被删除。
eg: HttpSessionBindingListener :比如往session中添加一个 User 对象,那就让 User 对象类实现该接口,当User对象从session中被删除时会回调通知该类方法valueUnbound(); — 指的是 属性对象本身
HttpSessionAttributeListener : 是往 session 对象的属性操作时,监听通知该类方法; — 指的是 session对象属性的变化,不是针对属性(任意属性都可以);
15.2 ServletContextListener 接口
有时候我们可能希望在Web 应用程序启动时执行一些初始化的任务,那么我们可以编写一个实现了ServletContextListener 接口的监听器类。
ServletContextListener 接口提供了下面的方法:
-> public void contextInitialized(ServletContextEvent sce)
当Web应用程序初始化进程正开始时,Web容器调用这个方法。该方法将在所有的过滤器和Servlet初始化之前被调用。
-> public void contextDestroyed(ServletContextEvent sce)
当Servlet 上下文将要被关闭时,Web容器调用这个方法。该方法将在所有的Servlet和过滤器销毁之后被调用。
Web容器通过 ServletContextEvent 对象来通知实现了 ServletContextListener 接口的监听器,监听器可以利用ServletContextEvent对象来得到 ServletContext对象。
javax.servlet.ServletContextEvent类除了构造方法外,只定义了一个方法,如下所示:
-> public ServletContext getServletContext()
该方法用于得到 ServletContext对象。
我们看一个实现了ServletContextListener 接口的监听器例子。我们在Web应用程序启动时初始化DataSource 对象,然后将它放到ServletContext中,Servlet 从 ServletContext中访问 DataSource对象,进而得到数据库连接,访问数据库。代码如例 15-1 所示。
package cn.itguigu.listener;import javax.naming.Context;import javax.naming.InitialContext;import javax.naming.NamingException;import javax.servlet.ServletContext;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;import javax.sql.DataSource;/*** @PackageName:cn.itguigu.listener* @ClassName:MyServletContextListener* @Description:* @Author:lyg* @Date:2021/12/28 10:47*/public class MyServletContextListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {ServletContext sc = sce.getServletContext();// 得到 Servlet 上下文参数String jndi = sc.getInitParameter("jndi");Context ctx;try {ctx = new InitialContext();DataSource ds = (DataSource) ctx.lookup(jndi);// 将DataSource对象保存为ServletContext的属性sc.setAttribute("dataSource", ds);} catch (NamingException e) {e.printStackTrace();}}@Overridepublic void contextDestroyed(ServletContextEvent sce) {}}
在Servlet 中可以 使用如例 15-2所示的代码来访问 ServletContext 中的DataSource 对象,进而获取数据库连接,访问数据库。
例 15-2 在Servlet 中访问ServletContext中的DataSource 对象
DataSource ds = (DataSource) sc.getAttribute("dataSource");try {Connection conn = ds.getConnection();Statement stmt = conn.createStatement();ResultSet resultSet = stmt.executeQuery("....");} catch (SQLException throwables) {throwables.printStackTrace();}
要让 Web 容器在 Web应用程序启动时通知 MyServletContextListener, 你需要在web.xml 文件中使用
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaeehttp://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"version="3.1"><display-name>liuyaguang Archetype Created Web Application web.xml</display-name><context-param><param-name>jndi</param-name><param-value>java:comp/env/jdbc/bookstore</param-value></context-param><listener><listener-class>cn.itguigu.listener.MyServletContextListener</listener-class></listener></web-app>
在配置监听器时,不需要告诉容器你实现了什么监听器接口,容器自己会发现。
15.3 HttpSessionBindingListener 接口
如果一个对象实现了 HttpSessionBindingListener接口,当这个对象被绑定到Session 中或者从Session中被删除时,Servlet 容器会通知这个对象,而这个对象在接收到通知后,可以做一些初始化或清除状态的操作。
例如,在网络购物应用中,可以让购物车对象实现HttpSessionBindingListener接口,当顾客选购商品时,Web应用程序创建购物车对象,保存在Session中,并在购物车中放入预选的商品。当顾客没有结账就离开了站点,或者顾客在浏览商品介绍的时候,Session超时,这个时候,Servlet容器就会通知购物车对象,它要从Session中被删除了。购物车对象在得到通知后,可以把顾客选购的商品信息保存到数据库中。当顾客再次来到网站购物的时候,Web应用程序再将购物车对象保存(绑定)到Session中时,Servlet容器会通知购物车对象,此时,购物车对象可以从数据库中加载先前保存的商品信息。顾客会惊奇地发现,以前预购的商品信息仍然存在。
javax.servlet.http.HttpSessionBindingListener接口提供了下面的方法:
-> public void valueBound(HttpSessionBindingEvent event)
当对象正在被绑定到Session 中,Serlvet容器调用这个方法来通知该对象。
-> public void valueUnbound(HttpSessionBindingEvent event)
当 从Session 中删除对象时,Servlet容器调用这个方法来通知该对象。
servlet 容器 通过HttpSessionBindingEvent对象来通知实现了HttpSessionBindingListener接口的对象,而该对象可以利用 HttpSessionBindingEvent 对象来访问与它相联系的HttpSession对象。javax.servlet.http.HttpSessionBindingEvent 类提供了以下两种方法:
-> public HttpSessionBindingEvent(HttpSession session, java.lang.String name)
-> public HttpSessionBindingEvent(HttpSession session,java.lang.String name, java.lang.Object value)
上面两个构造方法构造一个事件对象,当一个对象被绑定到 Session 中或者从Session 中被删除时,用这个事件对象来通知它。
-> public java.lang.String getName()
返回绑定到Session 中或者从Session中删除的属性的名字。
-> public java.lang.Object getValue()
返回 被添加、删除、替换的属性值。如果属性被替换,这个方法返回属性先前的值。
-> public HttpSession getSession()
返回HttpSession 对象。
15.4 在线人数统计程序
下面,我们利用HttpSessionBingdingListener 接口,编写一个在线人数统计的程序。当一个用户登录后,显示欢迎信息,同时显示出当前在线的总人数和 用户名单。当一个用户退出登录或者Session 超时值发生时,从在线用户名单中删除这个用户,同时将在线总人数减 1. 这个功能的完成,主要是利用一个实现了 HttpSessionBindingListener 接口的对象,当这个对象被绑定到Session中或者从 Session中被删除时,更新当前在线的用户名单。
step 1 : 配置web应用程序的运行目录
在%CATALINA_HOME%\conf\Catalina\localhost\目录下新建ch15.xml文件,输入如例15-4 所示的内容。
<Context docBase="F:\JSPLesson\ch15" reloadable="true" />
step 2 : 编写login.html
将编写好的login.html文件放到 F:\xxxx\xxx 目录下 。完整的代码编写如例:15-5 所示
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><form action="onlineUserServlet" method="post"><table><tr><td>请输入用户名</td><td><input type="text" name="user"></td></tr><tr><td>请输入密码</td><td><input type="password" name="password"></td></tr><tr><td><input type="reset" value="重置"></td><td><input type="submit" value="登录"></td></tr></table></form></body></html>
step 3 : 编写UserList.java , User.java, OnlineUserServlet.java 和 LogoutServlet.java
public class UserList {private static final UserList userList = new UserList();private Vector<String> v;private UserList(){v = new Vector<>();}public static UserList getInstance() {return userList;}public void addUser(String name) {if (name != null) {v.addElement(name);}}public void removeUser(String name) {if (name != null) {v.remove(name);}}public Enumeration<String> getUserList(){return v.elements();}public int getUserCount() {return v.size();}}
public class User implements HttpSessionBindingListener {private String name;private UserList ul = UserList.getInstance();public User() {}public User(String name) {this.name = name;}public void setName(String name) {this.name = name;}public String getName() {return name;}@Overridepublic void valueBound(HttpSessionBindingEvent event) {System.out.println("将用户: "+ name +"添加到session对象中" + "HttpSessionBindingEvent" + event.toString());ul.addUser(name);}@Overridepublic void valueUnbound(HttpSessionBindingEvent event) {System.out.println("将用户: "+ name +"添加到session对象中" + "HttpSessionBindingEvent" + event.toString());ul.removeUser(name);}}
public class OnlineUserServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {req.setCharacterEncoding("gb2312");String name = req.getParameter("user");String pwd = req.getParameter("password");if (name == null || pwd == null || name.equals("") || pwd.equals("")) {resp.sendRedirect(req.getContextPath() + "/itguigu/login.html");} else {HttpSession session = req.getSession();User user = (User)session.getAttribute("user");if (null == user || !name.equals(user.getName())) {user = new User(name);session.setAttribute("user", user);}resp.setContentType("text/html;charset=gb2312");PrintWriter out = resp.getWriter();out.println("欢迎用户<b> " + name +"</b> 登录");final UserList ul = UserList.getInstance();out.println("<br> 当前在线的用户列表: <br>");final Enumeration<String> enums = ul.getUserList();int i =0 ;while (enums.hasMoreElements()) {out.println(enums.nextElement());out.println(" ");if (++i == 10) {out.println("<br>");}}out.println("<br> 当前在线的用户数:" + i);out.println("<p><a href=" + req.getContextPath() + "/itguigu/logout> 退出登录");out.close();}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {doGet(req, resp);}}
public class LogoutServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {resp.setContentType("text/html;charset=gb2312");HttpSession session = req.getSession();User user = (User) session.getAttribute("user");session.invalidate();PrintWriter out = resp.getWriter();out.println("<html><head><title> 退出登录</title></head><body>");out.println(user.getName() + ",你已退出登录<br>");out.println("<a href="+ req.getContextPath() +"/itguigu/login.html");out.close();}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {doGet(req, resp);}}
step 4 : 编译 上述4 个Java 源文件
step 5: 部署 Servlet
step 6 : 运行在线人数统计程序

一、Listener (监听器)
二 、分类
2.1、监听域对象的创建 和 销毁

2.1.1、ServletContextListener (ServletContext 域对象 监听器)
2.1.2、HttpSessionListener (HttpSession 域对象 监听器)
2.1.3 、ServletRequestListener (ServletRequest 域对象 监听器)



2.2 、域对象中属性的变更的事件监听


2.3、 感知Session 绑定的事件监听器



2.4、HttpSessionActivationListener 接口 感知 JavaBean 的活化 和 钝化


