1.监听器概述

监听器用于监听web应用中某些对象、信息的创建、销毁、增加,修改,删除等动作的发生,然后作出相应的响应处理。当范围对象的状态发生变化的时候,服务器自动调用监听器对象中的方法。常用于统计在线人数和在线用户,系统加载时进行信息初始化,统计网站的访问量等等。

监听器是一个对象,用来监听另一个对象的变化。

例如:
汽车上,安装一个报警器。
监听其他对象的对象叫做监听器。
被监听的对象叫做事件源。
报警器就是一个监听器。
汽车就是事件源。

触发  踹一脚汽车。

监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行.
JavaWeb开发专题(十五)-监听器_监听器

2.自己实现监听器(了解)

//观察者设计模式(通常用来处理事件系统)
//事件源
class Person{
private PersonListener listener;
public void registerListener(PersonListener listener){
this.listener = listener;
}
public void run(){
if(listener!=null){
Even even = new Even(this);
this.listener.dorun(even);
}
System.out.println("runn!!");
}
public void eat(){
if(listener!=null){
Even e = new Even(this);
this.listener.doeat(e);
}
System.out.println("eat!!");
}
}
//事件监听器
interface PersonListener{
public void dorun(Even even);
public void doeat(Even even);
}
//事件对象(封装事件源)
class Even{
private Person person;
public Even() {
super();
}
public Even(Person person) {
super();
this.person = person;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
public class Demo3 {

public static void main(String[] args) {
Person p = new Person();
p.registerListener(new MyListener1());
p.eat();
p.run();
}
}

class MyListener1 implements PersonListener{

public void doeat(Even even) {
System.out.println(even.getPerson()+"你天天吃,你就知道吃,你猪啊!!");
}
public void dorun(Even even) {
System.out.println(even.getPerson()+"你吃完就跑,有病!!");
}
}

ps:观察者模式的一个例子:求职者先在猎头处注册,当有新的工作机会时猎头就会通知求职者.

3.第一类:监听域对象的生命周期

JavaWeb中的监听器是Servlet规范中定义的一种特殊类,它用于监听web应用程序中的ServletContext, HttpSessionServletRequest等域对象的创建与销毁事件。

域对象

类型

范围

page

object

当前页面有效

Request

HttpServletRequest

一次请求中有效 (转发)

Session

HttpSession

一次会话中有效

Application

ServletConext

整个应用APP有效 存储的数据所有用户共享

3.1.ServletContextListener

ServletContextListener接口用于监听ServletContext对象的创建和销毁事件。实现了ServletContextListener接口的类都可以对ServletContext对象的创建和销毁进行监听。

当ServletContext对象被创建时,激发contextInitialized (ServletContextEvent sce)方法。
当ServletContext对象被销毁时,激发contextDestroyed(ServletContextEvent sce)方法。

JavaWeb开发专题(十五)-监听器_监听器_02

  • 范例:编写一个MyServletContextListener类,实现ServletContextListener接口,监听ServletContext对象的创建和销毁
public class MyServletContextListener implements ServletContextListener {

public void contextInitialized(ServletContextEvent arg0) {
System.out.println("ServletContext 出生了....");
}

public void contextDestroyed(ServletContextEvent arg0) {
// ServletContext销毁时调用的方法
System.out.println("ServletContext 升天了....");
}
}
  • 在web.xml文件中注册监听器
    要想监听事件源,那么必须将监听器注册到事件源上才能够实现对事件源的行为动作进行监听,在JavaWeb中,监听的注册是在web.xml文件中进行配置的,如下:
<!-- 注册针对ServletContext对象进行监听的监听器 -->
<listener>
<!--实现了ServletContextListener接口的监听器类 -->
<listener-class>com.bruceliu.filter.MyServletContextListener</listener-class>
</listener>
  • 注解式配置
@WebListener
public class MyServletContextListener implements ServletContextListener {

public void contextInitialized(ServletContextEvent arg0) {
System.out.println("ServletContext 出生了....");
}

public void contextDestroyed(ServletContextEvent arg0) {
// ServletContext销毁时调用的方法
System.out.println("ServletContext 升天了....");
}
}
3.1.1.案例1:任务调度(使用 Timer和TimerTask)
@WebListener
public class TimerListener implements ServletContextListener {

public void contextInitialized(ServletContextEvent arg0) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("发邮件....");
}
}, 5000, 2000);
}

public void contextDestroyed(ServletContextEvent arg0) {

}
}

3.2.HttpSessionListener

HttpSessionListener 接口用于监听HttpSession对象的创建和销毁

创建一个Session时,激发sessionCreated (HttpSessionEvent se) 方法
销毁一个Session时,激发sessionDestroyed (HttpSessionEvent se) 方法。

JavaWeb开发专题(十五)-监听器_监听器_03

  • 范例:编写一个MyHttpSessionListener类,实现HttpSessionListener接口,监听HttpSession对象的创建和销毁
@WebListener
public class MyHttpSessionListener implements HttpSessionListener {

@Override
public void sessionCreated(HttpSessionEvent arg0) {
// arg0.getSession(); 获取事件源
System.out.println("Session创建了<<<<<");
}

@Override
public void sessionDestroyed(HttpSessionEvent arg0) {
System.out.println("Session销毁<<<<<");
}
}

HttpSession的销毁时机需要在web.xml中进行配置,如下:

<session-config>
<session-timeout>1</session-timeout>
</session-config>
  • 当我们访问jsp页面时,HttpSession对象就会创建,此时就可以在HttpSessionListener观察到HttpSession对象的创建过程了,我们可以写一个jsp页面观察HttpSession对象创建的过程。
    如下:index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<title>HttpSessionListener监听器监听HttpSession对象的创建</title>
</head>
<body>
session已经创建对象:${pageContext.session.id}<br/>
<%=session.getId()%>
</body>
</html>
3.2.1.全网在线人数统计

所有用户看到的在线人数都是一样的!那么在线人数这个数据保存在哪里?
ServletContext对象中!

在线人数:session的个数,当用户访问服务器时,服务器就会为当前用户创建一个session对象,因此session的个数就是在线人数!

实现思路:创建session对象的监听器,当session创建时,人数+1,销毁时,人数-1;

  • session的创建和销毁的监听器:
@WebListener
public class OnlineListener implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent hse) {
// 从ServletContext对象中获取当前的在线人数
ServletContext context = hse.getSession().getServletContext();
Long online = (Long)context.getAttribute("online");

// 判断online是否为null:说明是第一个用户
if ( online == null ) {
//online = 45751245L; // 初始化在线人数
online = 0L; // 初始化在线人数
}

// 人数+1
online++;
context.setAttribute("online", online);
}

public void sessionDestroyed(HttpSessionEvent hse) {
ServletContext context = hse.getSession().getServletContext();
Long online = (Long)context.getAttribute("online");
context.setAttribute("online", --online);
}
}
  • 显示在线人数的online.jsp
<%@ page contentType="text/html; charset=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>
当前的在线人数是:${online }
</body>
</html>

4.第二类:监听值变化

域对象中属性的变更的事件监听器就是用来监听 ServletContext, HttpSession, HttpServletRequest 这三个对象中的属性变更信息事件的监听器。
这三个监听器接口分别是ServletContextAttributeListener, HttpSessionAttributeListenerServletRequestAttributeListener,这三个接口中都定义了三个方法来处理被监听对象中的属性的增加,删除和替换的事件,同一个事件在这三个接口中对应的方法名称完全相同,只是接受的参数类型不同。

4.1.ServletContextAttributeListener

JavaWeb开发专题(十五)-监听器_java_04

  • 编写ServletContextAttributeListener监听器监听ServletContext域对象的属性值变化情况,代码如下:
@WebListener
public class MyServletContextAttributeListener implements ServletContextAttributeListener {

@Override
public void attributeAdded(ServletContextAttributeEvent scab) {
String str = MessageFormat.format("ServletContext域对象中添加了属性:{0},属性值是:{1}", scab.getName(), scab.getValue());
System.out.println(str);
}

@Override
public void attributeRemoved(ServletContextAttributeEvent scab) {
String str = MessageFormat.format("ServletContext域对象中删除属性:{0},属性值是:{1}", scab.getName(), scab.getValue());
System.out.println(str);
}

@Override
public void attributeReplaced(ServletContextAttributeEvent scab) {
String str = MessageFormat.format("ServletContext域对象中替换了属性:{0}的值", scab.getName());
System.out.println(str);
}
}
  • 编写test.jsp测试页面
<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<title>ServletContextAttributeListener监听器测试</title>
</head>

<body>
<%
//往application域对象中添加属性
application.setAttribute("name", "哈哈");
//替换application域对象中name属性的值
application.setAttribute("name", "呵呵");

//移除application域对象中name属性
application.removeAttribute("name");
%>
</body>
</html>

4.2.HttpSessionAttributeListener

JavaWeb开发专题(十五)-监听器_html_05

@WebListener
public class MySessionAttrListener implements HttpSessionAttributeListener {

@Override
public void attributeAdded(HttpSessionBindingEvent se) {
String str = MessageFormat.format("HttpSession域对象中添加了属性:{0},属性值是:{1}", se.getName(), se.getValue());
System.out.println(str);
}

@Override
public void attributeRemoved(HttpSessionBindingEvent se) {
String str = MessageFormat.format("HttpSession域对象中删除属性:{0},属性值是:{1}", se.getName(), se.getValue());
System.out.println(str);
}

@Override
public void attributeReplaced(HttpSessionBindingEvent se) {
String str = MessageFormat.format("HttpSession域对象中替换了属性:{0}的值", se.getName());
System.out.println(str);
}
}
  • 编写test1.jsp测试页面
<html>
<head>
<title>ServletContextAttributeListener监听器测试</title>
</head>

<body>
<%
//往session域对象中添加属性
session.setAttribute("aa", "bb");
//替换session域对象中aa属性的值
session.setAttribute("aa", "xx");
//移除session域对象中aa属性
session.removeAttribute("aa");
%>
</body>
</html>

4.3.ServletRequestAttributeListener

JavaWeb开发专题(十五)-监听器_监听器_06

@WebListener
public class MyRequestAttributeListener implements ServletRequestAttributeListener {

@Override
public void attributeAdded(ServletRequestAttributeEvent srae) {
String str = MessageFormat.format("ServletRequest域对象中添加了属性:{0},属性值是:{1}", srae.getName(), srae.getValue());
System.out.println(str);
}

@Override
public void attributeRemoved(ServletRequestAttributeEvent srae) {
String str = MessageFormat.format("ServletRequest域对象中删除属性:{0},属性值是:{1}", srae.getName(), srae.getValue());
System.out.println(str);
}

@Override
public void attributeReplaced(ServletRequestAttributeEvent srae) {
String str = MessageFormat.format("ServletRequest域对象中替换了属性:{0}的值", srae.getName());
System.out.println(str);
}
}
  • 编写test2.jsp测试页面
<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<title>ServletContextAttributeListener监听器测试</title>
</head>

<body>
<%
//往request域对象中添加属性
request.setAttribute("aa", "bb");
//替换request域对象中aa属性的值
request.setAttribute("aa", "xx");
//移除request域对象中aa属性
request.removeAttribute("aa");
%>
</body>
</html>

5.监听器小结

JavaWeb开发专题(十五)-监听器_监听器_07

6.第三类:监听HttpSession中的对象的(JavaBean)面试题

前两类监听器是作用在 ServletContext HttpSession ServletRequest上的,第三类监听器是作用在JavaBean上的。
这类监听器不需要在web.xml中配置。

6.1.HttpSessionBindingListener

实现了HttpSessionBindingListener接口的JavaBean对象可以感知自己被绑定到Session中和 Session中删除的事件

当对象被绑定到HttpSession对象中时,web服务器调用该对象的void valueBound(HttpSessionBindingEvent event)方法
当对象从HttpSession对象中解除绑定时,web服务器调用该对象的void valueUnbound(HttpSessionBindingEvent event)方法

public class User implements HttpSessionBindingListener {

private int id;
private String name;

public User() {

}

public User(int id, String name) {
super();
this.id = id;
this.name = name;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public void valueBound(HttpSessionBindingEvent arg0) {
System.out.println("对象绑定到了Session中");
}

public void valueUnbound(HttpSessionBindingEvent arg0) {
System.out.println("对象从Session中移除");
}
}

测试页面

<%@ page import="com.bruceliu.bean.User"%>
<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<title>ServletContextAttributeListener监听器测试</title>
</head>

<body>
<%
User user = new User(1, "aaa");
session.setAttribute("user", user);
session.removeAttribute("user");
%>
</body>
</html>

6.2.HttpSessionActivationListener 监听session钝化和活化

实现了HttpSessionActivationListener接口的JavaBean对象可以感知自己被活化(反序列化)和钝化(序列化)的事件.当绑定到HttpSession对象中的javabean对象将要随HttpSession对象被钝化(序列化)之前,web服务器调用该javabean对象的void sessionWillPassivate(HttpSessionEvent event) 方法。这样javabean对象就可以知道自己将要和HttpSession对象一起被序列化(钝化)到硬盘中.

当绑定到HttpSession对象中的javabean对象将要随HttpSession对象被活化(反序列化)之后,web服务器调用该javabean对象的void sessionDidActive(HttpSessionEvent event)方法。这样javabean对象就可以知道自己将要和 HttpSession对象一起被反序列化(活化)回到内存中
JavaWeb开发专题(十五)-监听器_监听器_08

放在Session中的 没有实现Serilizable接口的对象,在Session钝化时,不会被序列化到磁盘上。

public class Person implements Serializable, HttpSessionActivationListener {
private int id;
private String name;

public Person() {

}

public Person(int id, String name) {
super();
this.id = id;
this.name = name;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public void sessionDidActivate(HttpSessionEvent arg0) {
// 活化
System.out.println("对象被活化......");
}

public void sessionWillPassivate(HttpSessionEvent arg0) {
// 钝化
System.out.println("对象被钝化.......");
}
}

为了观察绑定到HttpSession对象中的javabean对象随HttpSession对象一起被钝化到硬盘上和从硬盘上重新活化回到内存中的的过程,我们需要借助tomcat服务器帮助我们完成HttpSession对象的钝化和活化过程,具体做法如下:

在WebRoot\META-INF文件夹下创建一个context.xml文件,如下所示:
JavaWeb开发专题(十五)-监听器_监听器_09

<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!-- maxIdleSwap:1 session如果1分钟没有使用 就序列化. directory:序列化后 文件所保存的路径. -->
<Manager className="org.apache.catalina.session.PersistentManager"
maxIdleSwap="1">
<Store className="org.apache.catalina.session.FileStore" directory="bruce123" />
</Manager>
</Context>

在context.xml文件文件中配置了1分钟之后就将HttpSession对象钝化到本地硬盘的一个bruce123文件夹中

jsp测试代码如下:

<%@page import="com.bruceliu.bean.Person"%>
<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<title>测试</title>
</head>

<body>
<%
Person p = new Person(1, "zhangsan");
session.setAttribute("p", p);
%>
</body>
</html>

访问这个jsp页面,服务器就会马上创建一个HttpSession对象,然后将实现了HttpSessionActivationListener接口的JavaBean对象绑定到session对象中,这个jsp页面在等待1分钟之后没有人再次访问,那么服务器就会自动将这个HttpSession对象钝化(序列化)到硬盘上,我们可以在tomcat服务器文件夹下找到序列化到本地存储的session,如下图所示:
JavaWeb开发专题(十五)-监听器_html_10
当再次访问这个Jsp页面时,服务器又会自动将已经钝化(序列化)到硬盘上HttpSession对象重新活化(反序列化)回到内存中。运行结果如下:
JavaWeb开发专题(十五)-监听器_监听器_11