15. 监听器
在Web容器运行过程中,有很多关键点事件,比如Web应用被启动、用户会话开始、用户会话结束、用户请求到达等,Servlet API提供了大量监听器接口来帮助开发者实现对Web应用内特定事件进行监听,从而当Web应用内这些特定事件发生时,回调监听器内的事件监听方法来实现一些特殊功能,监听器的作用是监听Web容器的有效期事件,因此它是由容器管理的。
监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行。
在Servlet规范中定义了多种类型的监听器,它们用于监听的事件源分别为 ServletContext, HttpSession 和 ServletRequest 这三个域对象。
Servlet规范针对这三个对象上的操作,又把这多种类型的监听器划分为三种类型。
- 监听三个域对象创建和销毁的事件监听器
- 监听域对象中属性的增加和删除的事件监听器
- 监听绑定到 HttpSession 域中的某个对象的状态的事件监听器。
下表列出了Servlet中的8个Listener接口和6个Event类。
Listener 接口 | Event 类 |
---|---|
ServletContextListener | ServletContextEvent |
ServletContextAttributeListener | ServletContextAttributeEvent |
HttpSessionListener | HttpSessionEvent |
HttpSessionActivationListener | |
HttpSessionAttributeListener | HttpSessionBindingEvent |
HttpSessionBindingListener | |
ServletRequestListener | ServletRequestEvent |
ServletRequestAttributeListener | ServletRequestAttributeEvent |
15.1 监听器创建与使用
监听器的实现通过两个步骤完成:
步骤一:定义监听器实现类,实现监听器接口的所有方法;
步骤二:通过Annotation或在web.xml文件中声明Listener。
注解@WebListener用于对监听器进行声明。
@WebListener
public class MyServletContextListener implements ServletContextListener {
与其等价的web.xml中的声明形式如下
<listener>
<listener-class>com.neuedu.listener. MyServletContextListener </listener-class>
</listener>
15.2 Servlet上下文监听器
与Servlet上下文相关的监听器需要实现的监听器接口
监听器接口名称 | 说明 |
---|---|
ServletContextListener | 用于监听ServletContext(application)对象的创建和销毁 |
ServletContextAttributeListener | 用于监听ServletContext(application)范围内属性的改变 |
ServletContextListener 接口用于监听 ServletContext 对象的创建和销毁事件。
- 在ServletContextListener接口中定义了如下两个事件处理方法:
- contextInitialized(ServletContextEvent sce)
- 当ServletContext对象被创建时,Web容器将调用此方法。
- contextDestroyed(ServletContextEvent sce)
- 当ServletContext对象被销毁时,Web容器调用此方法。
- contextInitialized(ServletContextEvent sce)
- 在ServletContextListener接口中定义了如下两个事件处理方法:
ServletContext域对象创建和销毁:
- 创建:服务器启动针对每一个web应用创建servletcontext
- 销毁:服务器关闭前先关闭代表每一个web应用的servletContext
实例代码
@WebListener
public class MyApplicationListener implements ServletContextListener {
Connection conn = null;
@Override
public void contextInitialized(ServletContextEvent sce) {
Object object = sce.getSource();
ServletContext application = sce.getServletContext();
String realPath = application.getRealPath("/");
System.out.println("应用被初始化,MyApplicationListener.contextInitialized");
System.out.println("应用发布的路径 :"+realPath);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("应用被销毁: MyApplicationListener.contextDestroyed");
}
}
15.3 HttpSession监听器
HttpSessionListener接口用于监听HttpSession的创建和销毁
在HttpSessionListener接口中定义了如下两个事件处理方法:
- sessionCreated(HttpSessionEvent se)
- 当HttpSession对象被创建时,Web容器将调用此方法。该方法接收HttpSessionEvent事件对象,通过此对象可获得当前被创建的HttpSession对象;
- sessionDestroyed(HttpSessionEvent se)
- 当HttpSession对象被销毁时,Web容器调用此方法,同时向其传递HttpSessionEvent事件对象。
- sessionCreated(HttpSessionEvent se)
应用案例:当前在线人数的统计功能
实例代码
15.3.1 MySessionListner
package com.neuedu.listener;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import java.util.ArrayList;
import java.util.List;
/**
* 项目: CrossDomain
* 类名: MySessionListner
* 创建时间: 2020/12/17 15:58
* 描述 : 会话监听
* 作者 : 张金山
* QQ : 314649444
* Site: https://jshand.gitee.io
*/
public class MySessionListner implements HttpSessionListener {
//当前登录的会话
private List<HttpSession> sessionList = new ArrayList();
@Override
public void sessionCreated(HttpSessionEvent se) {
ServletContext app = se.getSession().getServletContext();
app.setAttribute("sessionList",sessionList);
System.out.println("session创建: MySessionListner.sessionCreated");
sessionList.add(se.getSession());
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("session销毁: MySessionListner.sessionDestroyed");
sessionList.remove(se.getSession());
}
}
在web.xml中声明监听器
<listener>
<listener-class>com.neuedu.listener.MySessionListner</listener-class>
</listener>
15.3.2 session-user-list.jsp
<%@ page import="java.util.List" %><%--
Created by IntelliJ IDEA.
User: root
Date: 2020/12/17
Time: 16:07
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
List<HttpSession> sessionList = (List<HttpSession>) application.getAttribute("sessionList");
%>
<h3>当前在线人数:<%=sessionList.size()%></h3>
<table cellpadding="0" cellspacing="0" border="1" width="90%">
<tr>
<td>序号</td>
<td>登录时间</td>
</tr>
<%
int index = 0;
for (HttpSession httpSession : sessionList) {
%>
<tr>
<td><%=++index%></td>
<td><%=httpSession.getCreationTime()%></td>
</tr>
<%
}
%>
</table>
</body>
</html>
访问效果:
15.3.3 invalidate.jsp
<%--
Created by IntelliJ IDEA.
User: root
Date: 2020/12/17
Time: 16:02
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
session.invalidate();
%>
</body>
</html>
15.4 HttpRequest监听器
ServletRequestListener 接口用于监听ServletRequest 对象的创建和销毁。
ServletRequestListener接口中定义了如下两个事件处理方法:
- requestInitialized(ServletRequestEvent sre)
- 当ServletRequest对象被创建时,Web容器将调用此方法。该方法接收ServletRequestEvent事件对象,通过此对象可获得当前被创建的ServletRequest对象;
- requestDestroyed(ServletRequestEvent sre)
- 当ServletRequest对象被销毁时,Web容器调用此方法,同时向其传递ServletRequestEvent事件对象。
javapackage com.neuedu.listener; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpServletRequest; /** * 项目: CrossDomain * 类名: MyServletRequestListener * 创建时间: 2020/12/17 16:40 * 描述 : * 作者 : 张金山 * QQ : 314649444 * Site: https://jshand.gitee.io */ @WebListener public class MyServletRequestListener implements ServletRequestListener { @Override public void requestDestroyed(ServletRequestEvent sre) { System.out.println("请求对象被销毁:MyServletRequestListener.requestDestroyed"); } @Override public void requestInitialized(ServletRequestEvent sre) { HttpServletRequest request = (HttpServletRequest) sre.getServletRequest(); System.out.println("请求对象被创建:MyServletRequestListener.requestInitialized"); System.out.println("请求:路径:"+request.getRequestURL().toString()); System.out.println("请求:路径:"+request.getRequestURI()); } }
- requestInitialized(ServletRequestEvent sre)
15.5 监听三个域对象属性变化
Servlet规范定义了监听 ServletContext, HttpSession, HttpServletRequest 这三个对象中的属性变更信息事件的监听器。
- 这三个监听器接口分别是
- ServletContextAttributeListener,
- HttpSessionAttributeListener
- ServletRequestAttributeListener
- 这三个接口中都定义了三个方法来处理被监听对象中的属性的增加,删除和替换的事件,同一个事件在这三个接口中对应的方法名称完全相同,只是接受的参数类型不同。
- 方法示例:
- public void attributeAdded(ServletContextAttributeEvent scae)
- public void attributeReplaced(HttpSessionBindingEvent hsbe)
- public void attributeRmoved(ServletRequestAttributeEvent srae)
package com.neuedu.listener;
import javax.servlet.*;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
/**
* 项目: CrossDomain
* 类名: MyAttributeListener
* 创建时间: 2020/12/17 16:51
* 描述 : 监听域对象 属性的变化 attribute
* 作者 : 张金山
* QQ : 314649444
* Site: https://jshand.gitee.io
*/
@WebListener
public class MyAttributeListener implements ServletContextAttributeListener , HttpSessionAttributeListener , ServletRequestAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent event) {
ServletContext app = event.getServletContext();
String attributeName = event.getName();
Object value = event.getValue();
System.out.println(String.format("\r\n属性添加 name: %s\t value :%s ",attributeName,value.toString()));
}
@Override
public void attributeRemoved(ServletContextAttributeEvent event) {
String attributeName = event.getName();
Object value = event.getValue();
System.out.println(String.format("\r\n属性 remove name: %s\t value :%s ",attributeName,value.toString()));
}
@Override
public void attributeReplaced(ServletContextAttributeEvent event) {
String attributeName = event.getName();
Object oldValue = event.getValue(); //zs
Object value = event.getServletContext().getAttribute(attributeName); //li
System.out.println(String.format("\r\n属性replace name: %s\t value :%s \t newValue:%s",attributeName,oldValue.toString(), value.toString()));
}
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
}
@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
}
@Override
public void attributeReplaced(HttpSessionBindingEvent event) {
}
@Override
public void attributeAdded(ServletRequestAttributeEvent srae) {
}
@Override
public void attributeRemoved(ServletRequestAttributeEvent srae) {
}
@Override
public void attributeReplaced(ServletRequestAttributeEvent srae) {
}
}
15.6 Session绑定的事件监听器
保存在 Session 域中的对象可以有多种状态:
- 绑定到 Session 中;
- 从 Session 域中解除绑定;
- 随 Session 对象持久化到一个存储设备中(钝化);
- 钝化是指服务器会将不常使用的Session对象暂时序列化到系统文件或数据库中。
- 随 Session 对象从一个存储设备中恢复(活化)
- 活化就是将暂存在系统文件或数据库中的Session对象反序列化到服务器中,当Tomcat服务器被关闭或者重启时,Tomcat会将Session对象钝化到服务器文件系统中,当服务器被重新加载时,Session对象也会被活化。
- Servlet 规范中定义了两个特殊的监听器接口来帮助 JavaBean 对象了解自己在 Session 域中的这些状态
- HttpSessionBindingListener接口
- HttpSessionActivationListener接口
这一类监听器在被定义之后不需要在进行声明注册
实现了HttpSessionBindingListener接口的 JavaBean 对象可以感知自己被绑定到 Session 中和从 Session 中删除的事件
当对象被绑定到 HttpSession 对象中时,web 服务器调用该对象的 void valueBound(HttpSessionBindingEvent event) 方法
当对象从 HttpSession 对象中解除绑定时,web 服务器调用该对象的 void valueUnbound(HttpSessionBindingEvent event)方法
这里绑定对象与解除绑定使用的仍然是Session的setAttribute和removeAttribute方法,这和前面设置属性是一样的,那么这种绑定对象的监听器与设置属性的监听器有什么区别呢?绑定对象的监听器只监听某种类型的对象的绑定与解绑操作,而设置属性的监听器监听的是所有设置属性的动作。
