监听器的介绍

时间:2021-12-12 08:37:00

何谓监听器?

监听器就是监听某个对象的的状态变化的组件

监听器的相关概念:

事件源:被监听的对象有三个域对象:request,session,servletContext。

监听器:监听事件源对象事件源对象的状态的变化都会触发监听器。

注册监听器:将监听器与事件源进行绑定。

响应行为:监听器监听到事件源的状态变化时,所涉及的功能代码 ---- 程序员编写代码。

监听器有哪些?

第一维度:按照被监听的对象划分:ServletRequest域   HttpSession域    ServletContext域。

第二维度:按照监听的内容分为监听域对象的创建与销毁的监听域对象的属性变化的。

监听器的介绍

监听三大域对象的创建与销毁的监听器

监听ServletContext域的创建和销毁的监听器——ServletContextListener

Servlet的生命周期

创建:在服务器启动的时候自动创建

销毁:在服务器关闭的时候销毁

监听器的编写步骤

1.编写一个监听器类去实现监听器接口

2.覆盖监听器的方法

3.需要在web.xml中进行配置——注册

监听的方法

 contextInitialized(ServletContextEvent servletContextEvent)——servletContext创建的时候执行,其中ServletContextEvent参数是事件源对象,也就是被监听的对象

 contextDestroyed(ServletContextEvent servletContextEvent)——servletContext被销毁的时候执行

配置文件

<listener>
   <listener-class>com.hzhiping.listener.Demo04ServletContextAttributeListener</listener-class>
</listener>

监听器ServletContextListener的主要作用

初始化工作:初始化对象,初始化数据——加载数据库驱动,连接池的初始化;

加载一些初始化的配置文件——spring的配置文件;

任务调度:timer/timerTask。

案例

/**
 * Copyright (C), 2018, hzhiping
 * Title:Demo01ServletContextListener
 * Author:hzhiping
 * Date:2018/12/13 9:42
 * Description: servletContext监听器实例
 */
package com.hzhiping.listener;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class Demo01ServletContextListener implements ServletContextListener {
    // 监听ServletContext域名的创建
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        // 获取被监听的对象
        ServletContext servletContext = servletContextEvent.getServletContext();
        ServletContext source = (ServletContext) servletContextEvent.getSource();

        System.out.println("servletContext创建了...");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("servletContext销毁了...");
    }
}

监听ServletContext域,然后在web.xml文件中添加相关的配置:

监听器的介绍

此时发现当服务器启动就会创建servletContext。

监听Httpsession域的创建于销毁的监听器HttpSessionListener

HttpSession对象的生命周期

何时创建:第一次调用request.getSession时创建

何时销毁:服务器关闭销毁  session过期  手动销毁

方法

监听器的介绍

案例

/**
 * Copyright (C), 2018, hzhiping
 * Title:Demo02HttpSessionListener
 * Author:hzhiping
 * Date:2018/12/13 9:59
 * Description: 监听HttpSession的监听器
 */
package com.hzhiping.listener;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class Demo02HttpSessionListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        System.out.println(httpSessionEvent.getSession() + "创建了...");
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        System.out.println(httpSessionEvent.getSession() + "销毁了...");
    }
}

同样在配置文件中加上:

监听器的介绍

监听ServletRequest域创建于销毁的监听器ServletRequestListener

ServletRequest的生命周期

创建:每一次请求都会创建request

销毁:请求结束

ServletRequestListener的方法

/**
 * Copyright (C), 2018, hzhiping
 * Title:Demo03ServletRequestListender
 * Author:hzhiping
 * Date:2018/12/13 10:17
 * Description: ServletRequest监听器
 */
package com.hzhiping.listener;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;

public class Demo03ServletRequestListender implements ServletRequestListener {
    @Override
    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
        servletRequestEvent.getServletRequest();
        System.out.println("请求销毁了");
    }

    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        System.out.println("请求创建了");
    }
}

加上配置:

监听器的介绍

当我们访问index.jsp页面的时候就会发现结果如下:

监听器的介绍

这说明了,当我们发起一个请求的时候,先访问到ServletRequest监听器,然后先后修改ServletRequest和ServletContext中的相关属性,接着会访问HttpSession域,然后将请求销毁。

监听三大域对象的属性的变化的

域对象通用的方法

setAttribute(name,value)

 --- 触发添加属性的监听器的方法   

 --- 触发修改属性的监听器的方法

getAttribute(name)

removeAttribute(name)  

--- 触发删除属性的监听器的方法

ServletContextAttributeListener监听器

/**
 * Copyright (C), 2018, hzhiping
 * Title:Demo04ServletContextAttributeListener
 * Author:hzhiping
 * Date:2018/12/13 10:36
 * Description: 测试ServletContext对象的属性变化,对应ServletContextAttributeTest.jsp页面
 */
package com.hzhiping.listener;

import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;

public class Demo04ServletContextAttributeListener implements ServletContextAttributeListener {
    // 监听元素对象添加
    @Override
    public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {
        System.out.println("ServletContext域中添加的属性名称和对应的属性值分别为:" + servletContextAttributeEvent.getName() + ";" + servletContextAttributeEvent.getValue());
    }

    // 监听元素对象删除
    @Override
    public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {
        System.out.println("ServletContext域中删除的属性名称和对应的属性值分别为:" + servletContextAttributeEvent.getName() + ";" + servletContextAttributeEvent.getValue());
    }

    // 监听元素对象修改
    @Override
    public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {
        System.out.println("ServletContext域中修改的属性名称和对应的属性值分别为:" + servletContextAttributeEvent.getName() + ";" + servletContextAttributeEvent.getValue());
    }
}

添加配置,此处就不再赘述。这时候我们编写一个jsp进行对相关的域设置和修改:

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2018/12/13
  Time: 10:39
  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>
<%
    // 设置添加attribute
    application.setAttribute("name", "hzhiping");
    // 设置修改attribute
    application.setAttribute("name", "hzhihui");
    // 移除attribute
    application.removeAttribute("name");
%>
</body>
</html>

这时候当你访问上述的jsp页面的时候出现结果如下:

监听器的介绍

HttpSessionAttributeListener监听器

本监听器和下面的监听器一起。

ServletRequestAttributeListener监听器

/**
 * Copyright (C), 2018, hzhiping
 * Title:Demo05HttpSessionAttributeListener
 * Author:hzhiping
 * Date:2018/12/13 10:53
 * Description: 监听HttpSession以及ServletRequest的元素对象创建,修改和删除
 */
package com.hzhiping.listener;

import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

// 实现多个接口
public class Demo05HttpSessionAttributeListener implements HttpSessionAttributeListener, ServletRequestAttributeListener {
    // HttpSession
    @Override
    public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
        String httpsession = "HttpSession域中添加的属性名称和对应的属性值分别为:" +
                httpSessionBindingEvent.getName() + "," +
                httpSessionBindingEvent.getValue();
        System.out.println(httpsession);
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {
        String httpsession = "HttpSession域中删除的属性名称和对应的属性值分别为:" +
                httpSessionBindingEvent.getName() + "," +
                httpSessionBindingEvent.getValue();
        System.out.println(httpsession);
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {
        String httpsession = "HttpSession域中修改的属性名称和对应的属性值分别为:" +
                httpSessionBindingEvent.getName() + "," +
                httpSessionBindingEvent.getValue();
        System.out.println(httpsession);
    }

    // ServletRequest
    @Override
    public void attributeAdded(ServletRequestAttributeEvent servletRequestAttributeEvent) {
        String httpsession = "ServletRequest请求中添加的属性名称和对应的属性值分别为:" +
                servletRequestAttributeEvent.getName() + "," +
                servletRequestAttributeEvent.getValue();
        System.out.println(httpsession);
    }

    @Override
    public void attributeRemoved(ServletRequestAttributeEvent servletRequestAttributeEvent) {
        String httpsession = "ServletRequest请求中删除的属性名称和对应的属性值分别为:" +
                servletRequestAttributeEvent.getName() + "," +
                servletRequestAttributeEvent.getValue();
        System.out.println(httpsession);
    }

    @Override
    public void attributeReplaced(ServletRequestAttributeEvent servletRequestAttributeEvent) {
        String httpsession = "ServletRequest请求中修改的属性名称和对应的属性值分别为:" +
                servletRequestAttributeEvent.getName() + "," +
                servletRequestAttributeEvent.getValue();
        System.out.println(httpsession);
    }
}

同样在web中配置两个监听器的相关配置,然后编写jsp如下:

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2018/12/13
  Time: 10:39
  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.setAttribute("name", "hzhiping");
    session.setAttribute("name", "hzhihui");
    session.removeAttribute("name");

    request.setAttribute("name", "大傻春");
    request.setAttribute("name", "小傻春");
    request.removeAttribute("name");
%>
</body>
</html>

此时访问相应的jsp页面,就会出现相应的结果了。

与session中的绑定的对象相关的监听器(对象感知监听器)

即将要被绑定到session中的对象有几种状态?

绑定状态:就一个对象被放到session域中

解绑状态:就是这个对象从session域中移除了

钝化状态:是将session内存中的对象持久化(序列化)到磁盘

活化状态:就是将磁盘上的对象再次恢复到session内存中

Servlet 规范中定义了两个特殊的监听器接口"HttpSessionBindingListener"和"HttpSessionActivationListener"来帮助JavaBean 对象了解自己在Session域中的这些状态,实现这两个接口的类不需要 web.xml 文件中进行注册。

绑定和解绑的监听器HttpSessionBindingListener

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

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

同样的我们用一个案例来介绍:

/**
 * Copyright (C), 2018, hzhiping
 * Title:Demo06HttpSessionBindListener
 * Author:hzhiping
 * Date:2018/12/15 22:48
 * Description: 绑定
 */
package com.hzhiping.listener;

import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

public class Demo06HttpSessionBindListener implements HttpSessionBindingListener {
    private String name;

    public String getName() {
        return name;
    }

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

    public Demo06HttpSessionBindListener(String name) {
        this.name = name;
    }

    @Override
    public void valueBound(HttpSessionBindingEvent httpSessionBindingEvent) {
        System.out.println(name + "绑定了");
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) {
        System.out.println(name + "解绑了");
    }
}

然后我们知道这个是不用继续在web.xml中进行注册的,所以为了效果显示,我们直接再写一个jsp进行相关测试,当然也可以写一个servlet进行相关的测试:

<%@ page import="com.hzhiping.listener.Demo06HttpSessionBindListener" %><%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2018/12/15
  Time: 22:55
  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>
<%
    //将javabean对象绑定到Session中
    session.setAttribute("bean",new Demo06HttpSessionBindListener("hzhiping"));
    //从Session中删除javabean对象
    session.removeAttribute("bean");
%>
</body>
</html>

当我们访问这个页面的时候:

监听器的介绍

钝化和活化的监听器HttpSessionActivationListener

HttpSessionActivationListener简单介绍

接下来介绍的是钝化和活化的监听器HttpSessionActivationListener。

实现了HttpSessionActivationListener接口的JavaBean对象能够感知到自己被活化(反序列化)以及被钝化(序列化)的事件。

  • 活化【反序列化】:从磁盘中读取session对象
  • 钝化【序列化】:将session对象写入到到磁盘中

当写入到磁盘中的时候,session对象存储在 目录中

序列化和反序列化的概念

反序列化——将字节序列恢复成对象的过程称为对象的反序列化

序列化——将对象转换为字节序列的过程称为对象的序列化

序列化的用处

1)将对象的字节序列永久地保存在硬盘上,通常都会放置在文件中,下面我们会介绍

2)在网络上传送对象的字节序列

在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。

当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

当绑定到HttpSession对象中的javabean对象将要随HttpSession对象被钝化(序列化)之前,web服务器调用该javabean对象的void sessionWillPassivate(HttpSessionEvent event) 方法。这样javabean对象就可以知道自己将要和HttpSession对象一起被序列化(钝化)到硬盘中。

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

案例介绍

/**
 * Copyright (C), 2018, hzhiping
 * Title:Demo07HttpSessionActicationListener
 * Author:hzhiping
 * Date:2018/12/16 12:13
 * Description: 介绍对象的钝化和活化过程
 */
package com.hzhiping.listener;

import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;
import java.io.Serializable;// 需要实现这个类才能够将至钝化

public class Demo07HttpSessionActicationListener implements HttpSessionActivationListener, Serializable {
    private String name;

    public String getName() {
        return name;
    }

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

    @Override
    public void sessionWillPassivate(HttpSessionEvent httpSessionEvent) {
        System.out.println("对象" + name + "已经和" + httpSessionEvent.getSession().getId() + "一起钝化到硬盘中了");
    }

    @Override
    public void sessionDidActivate(HttpSessionEvent httpSessionEvent) {
        System.out.println("对象" + name + "已经和" + httpSessionEvent.getSession().getId() + "一起活化到内存中了");
    }

    public Demo07HttpSessionActicationListener(String name) {
        this.name = name;
    }
}

此时我们需要创建一个context.xml文件,未知如下:

监听器的介绍

内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
        <Store className="org.apache.catalina.session.FileStore" directory="D:\hzhiping"/>
    </Manager>
</Context>

<!--  以下是对上面的解释    -->  
<!-- path为项目的目录   docbase 为myeclipse目录  
    className   不能变   
    maxIdleSwap="1"  一分钟后钝化
    directory:目录
--> 

然后我们编写一个jsp页面来进行对象的保存:

<%@ page import="com.hzhiping.listener.Demo07HttpSessionActicationListener" %><%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2018/12/16
  Time: 12:21
  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>
访问本jsp页面,HttpSession创建完成,创建好的session的id为:${pageContext.session.id}
<%
    session.setAttribute("bean", new Demo07HttpSessionActicationListener("hzhiping"));
%>
</body>
</html>

虽然我们知道对象会被钝化,但是我们要怎么取出来并且确保取出来的就是我们钝化的那个对象呢?

此时我们再写一个jsp页面来获取:

<%@ page import="com.hzhiping.listener.Demo07HttpSessionActicationListener" %><%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2018/12/16
  Time: 12:51
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>将钝化的对象取出到内存中</title>
</head>
<body>
<%
    // 获取钝化对象
    Demo07HttpSessionActicationListener demo07HttpSessionActicationListener = (Demo07HttpSessionActicationListener) session.getAttribute("bean");
    // 输出名称
    System.out.println(demo07HttpSessionActicationListener.getName());
    // 输出sessionId
    System.out.println("活化的sessionId:" + session.getId());
%>
</body>
</html>

访问

监听器的介绍

等待一分钟后,我们发现已经被钝化了:

监听器的介绍

监听器的介绍

获取钝化对象的页面访问:

监听器的介绍

监听器的介绍

源码链接

本案例代码:仓库中的listener

坚壁清野