1 概述
Listener
用于监听Servlet
中的事件,例如context
、request
、session
对象的创建、修改、删除,并触发响应事件。Listener
是 观察者模式 的实现,在Servlet
中主要用于对context
、request
、session
对象的生命周期进行监控。在Servlet 2.5
规范中定义了8
种Listener
。
2 Servlet三大作用域
2.1 ServletContext (上下文对象)
(1)生命周期
- 创建:服务器启动时,为每个
web
项目创建一个上下文对象。 - 销毁:服务器关闭时,或者项目移除时。
(2)作用范围:
- 项目内共享,当前项目下所有程序都可以共享。
2.2 Request (请求对象)
(1)生命周期:
- 创建:请求开始的时候创建,每个请求都会对应自己的
request
对象。 - 销毁:请求结束,响应开始的时候。
(2)作用范围:
- 在一次请求中共享,只在当前请求中有效。
2.3 Session(会话对象)
(1)生命周期:
-
创建:在第一次调用
request.getSession()
方法时,web容器会检查是否已经有对应的session
对象存在,如果没有就创建一个session
对象。 -
销毁:
- 当一段时间内
session
没有被使用(默认30
分钟),被销毁; - 服务器非正常关闭(强行关闭)时销毁;
- 调用
session.invalidate()
手动销毁。
注意:服务器正常关闭,再启动,
Session
对象会进行 钝化 和 活化 操作。同时如果服务器钝化的时间在Session
默认销毁时间之内,则活化后Session
还是会存在的,否则Session
不存在。 如果JavaBean
数据在Session
钝化时,没有实现Serializable
,则当Session
活化时,会消失。 - 当一段时间内
(2)作用范围:
- 在一次会话中(多次请求)共享数据。
2.4 范围大小
ServletContext
>Session
>Request
3 Servlet中的8大监听器
3.1 分类
8
种Listener
如下表所示:
Listener接口 | Event类 |
---|---|
ServletContextListener | ServletContextEvent |
ServletContextAttributeListener | ServletContextAttributeEvent |
HttpSessionListener | HttpSessionEvent |
HttpSessionActivationListener | HttpSessionEvent |
HttpSessionAttributeListener | HttpSessionBindingEvent |
HttpSessionBindingListener | HttpSessionBindingEvent |
ServletRequestListener | ServletRequestEvent |
ServletRequestAttributeListener | ServletRequestAttributeEvent |
分为三类:
- (1)监听
Context
、Request
、Session
对象的创建和销毁,需要在web.xml
中配置ServletContextListener
ServletRequestListener
HttpSessionListener
- (2)监听
Context
、Request
、Session
对象属性的变化,需要在web.xml
中配置ServletContextAttributeListener
ServletRequestAttributeListener
HttpSessionAttributeListener
- (3)监听
Session
内部的对象,不需要再web.xml
中配置HttpSessionActivationListener
HttpSessionBindingListener
3.2 如何使用
- 实现
Listener
接口 - 在
web.xml
文件中配置- 使用
<listener>
标签和<listener-class>
标签 <listener>
一般配置在<servlet>
标签的前面
- 使用
package com.tao.springstarter.web.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* 自定义监听器, 实现ServletContextListener
*/
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("web容器启动, context对象被创建...");
System.out.println(sce.getServletContext().toString());
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("web容器关闭, context对象被销毁...");
}
}
web.xml
<!-- 配置监听器 -->
<listener>
<listener-class>com.tao.springstarter.web.listener.MyListener</listener-class>
</listener>
4 详细介绍
4.1 监听Context
、Request
、Session
对象的创建和销毁,需要在web.xml
中配置
-
ServletContextListener
-
ServletRequestListener
-
HttpSessionListener
4.1.1 作用和触发时机
(1) ServletContextListener
-
作用
- 监听
context
的创建和销毁,context
代表当前的Web
应用程序。 - 该
Listener
可用于启动时获取web.xml
里面配置的初始化参数。
- 监听
-
触发时机
- 服务器启动或者热部署
war
包时触发contextInitialized(ServletContextEvent sce)
方法。- 服务器关闭时或者只关闭该
web
应用时会触发contextDestroyed(ServletContextEvent sce)
方法。
(2)ServletRequestListener
-
作用
- 监听
request
的创建和销毁。
- 监听
-
触发时机
- 用户每次请求
request
都会触发requestInitialized(ServletRequestEvent sre)
方法。 request
处理完毕自动销毁前触发requestDestroyed(ServletRequestEvent sre)
方法。- 如果一个
HTML
页面包含多个图片,则请求一次HTML
页面可能会触发多次request
事件。
- 用户每次请求
(3)HttpSessionListener
-
作用
- 监听
Session
的创建于销毁。 - 该
Listener
可用于收集在线者信息。
- 监听
-
触发时机
- 创建
Session
时触发sessionCreated(HttpSessionEvent se)
方法。 - 超时或者执行
session.invalidate()
方法时执行sessionDestroyed(HttpSessionEvent se)
方法。
- 创建
4.1.2 实例
package com.tao.springstarter.web.listener;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import com.tao.springstarter.entity.JBean;
import com.tao.springstarter.entity.PBean;
/**
* 自定义监听器, 实现ServletContextListener
*/
public class MyListener implements ServletContextListener, ServletRequestListener, HttpSessionListener {
// ====================== context ==========================
// 加载 context
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("web容器启动, context对象被创建...");
// 可以获取 context对象
ServletContext context = sce.getServletContext();
System.out.println("即将启动 " + context.getContextPath());
}
// 卸载 context
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("web容器关闭, context对象被销毁...");
// 可以获取 context对象
ServletContext context = sce.getServletContext();
System.out.println("即将关闭 " + context.getContextPath());
}
// ====================== request ==========================
// 创建 request
@Override
public void requestInitialized(ServletRequestEvent sre) {
// 可以获取request对象
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
String uri = request.getRequestURI();
uri = request.getQueryString() == null ? uri : (uri + "?" + request.getQueryString());
System.out.println("请求的uri为:" + uri);
// 可以向request对象中放入这次请求中共享的数据
request.setAttribute("startTime", System.currentTimeMillis());
}
// 销毁 request
@Override
public void requestDestroyed(ServletRequestEvent sre) {
// 可以获取request对象, 获取request对象中的共享数据
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
long costTime = System.currentTimeMillis() - (Long) request.getAttribute("startTime");
System.out.println("本次请求耗时:" + costTime + " ms");
}
// ====================== session ==========================
// 创建 session
@Override
public void sessionCreated(HttpSessionEvent se) {
// 可以获取session对象
HttpSession session = se.getSession();
System.out.println("新创建一个session, id = " + session.getId());
// 向session中添加属性
session.setAttribute("username", "michael");
session.setAttribute("jBean", new JBean("JBean1"));
session.setAttribute("pBean", new PBean("PBean1"));
// 修改session中的属性
session.setAttribute("username", "Tom");
// 删除session中的属性
session.removeAttribute("username");
session.removeAttribute("jBean");
}
// 销毁 session
@Override
public void sessionDestroyed(HttpSessionEvent se) {
// 可以获取session对象
HttpSession session = se.getSession();
System.out.println("销毁一个session, id = " + session.getId());
}
}
4.2 监听Context
、Request
、Session
对象属性的变化,需要在web.xml
中配置
ServletContextAttributeListener
ServletRequestAttributeListener
HttpSessionAttributeListener
4.2.1 触发时机
- 当向被监听对象中添加、更新、移除属性时,会分别执行
attributeAdded()
、attributeReplaced()
、attributeRemoved()
方法。
4.2.2 实例
package com.tao.springstarter.web.listener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
/**
* 自定义session属性监听器
*/
public class MySessionAttributeListener implements HttpSessionAttributeListener {
// 向session中添加属性时触发
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
HttpSession session = event.getSession();
String key = event.getName();
Object value = event.getValue();
System.out.println("向id = " + session.getId() + " 的session中添加的属性为 <" + key + "," + value + ">");
}
// 从session中移除属性时触发
@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
HttpSession session = event.getSession();
String key = event.getName();
Object value = event.getValue();
System.out.println("从id = " + session.getId() + " 的session中移除的属性为 <" + key + "," + value + ">");
}
// 修改session中属性时触发
@Override
public void attributeReplaced(HttpSessionBindingEvent event) {
HttpSession session = event.getSession();
String key = event.getName();
System.out.println("修改id = " + session.getId() + " 的session的属性为 key = " + key);
}
}
4.3 监听Session
内部的对象,不需要在web.xml
中配置
保存在Session
域中的对象可以有多种状态:
-
绑定到
Session
中,session.setAttribute("bean",Object)
-
从
Session
域中解除绑定,session.removeAttribute("bean")
-
随
Session
对象持久化到一个存储设备中 -
随
Session
对象从一个存储设备中恢复
Servlet
规范中定义了两个特殊的监听器接口HttpSessionBindingListener
和HttpSessionActivationListener
来帮助JavaBean
对象了解自己在Session
域中的这些状态,实现这两个接口的对象类
不需要在web.xml
文件中进行注册。
4.3.1 HttpSessionBindingListener
- 实现了
HttpSessionBindingListener
接口的JavaBean
对象可以感知自己被绑定到Session
中和从Session
中删除的事件- 当对象被绑定到
HttpSession
对象中时,web
服务器调用该对象的valueBound(HttpSessionBindingEvent event)
方法- 当对象从
HttpSession
对象中解除绑定时,web
服务器调用该对象的valueUnbound(HttpSessionBindingEvent event)
方法
4.3.2 HttpSessionActivationListener
- 实现了
HttpSessionActivationListener
接口的JavaBean
对象可以感知自己被活化(反序列化)和钝化(序列化)的事件- 当绑定到
HttpSession
对象中的JavaBean
对象将要随HttpSession
对象被钝化**(序列化)**之前,web
服务器调用该JavaBean
对象的sessionWillPassivate(HttpSessionEvent event)
方法。这样JavaBean
对象就可以知道自己将要和HttpSession
对象一起被序列化(钝化)到硬盘中。- 当绑定到
HttpSession
对象中的JavaBean
对象将要随HttpSession
对象被活化**(反序列化)**之后,web
服务器调用该JavaBean
对象的sessionDidActive(HttpSessionEvent event)
方法。这样JavaBean
对象就可以知道自己将要和HttpSession
对象一起被反序列化(活化)回到内存中。
4.3.3 实例
(1)实现HttpSessionBindingListener
接口
package com.tao.springstarter.entity;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
/**
* 实现了HttpSessionBindingListener的类的对象能够感知被添加到session中和从session中移除的事件
*/
public class JBean implements HttpSessionBindingListener {
private String name;
public JBean(String name) {
this.name = name;
}
// JavaBean被加到session中触发
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println(name + "被加到session中了...");
}
// 从session中移除JavaBean时触发
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println(name + "从session中被移除了...");
}
}
上述的JBean
这个JavaBean
实现了HttpSessionBindingListener
接口,那么这个JavaBean
对象可以感知自己被绑定到Session
中和从Session
中删除的这两个操作。
(2)实现HttpSessionActivationListener
接口
package com.tao.springstarter.entity;
import java.io.Serializable;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;
/**
* 实现HttpSessionActivationListener接口的JavaBean, 能够感知随session钝化和活化事件
*/
public class PBean implements HttpSessionActivationListener, Serializable {
private static final long serialVersionUID = 1L;
private String name;
public PBean(String name) {
this.name = name;
}
// 钝化前触发
@Override
public void sessionWillPassivate(HttpSessionEvent se) {
System.out.println(name + "将要和session一起被序列化(钝化)到硬盘, session id = "+se.getSession().getId());
}
// 活化后触发
@Override
public void sessionDidActivate(HttpSessionEvent se) {
System.out.println(name + "和session一起从硬盘反序列化(活化)回到内存了, session id = "+se.getSession().getId());
}
}