监听器Listener

一、概述

1、概念

监听器就是一个实现了特定接口的Java类,主要用于监听某个对象的状态变化。

2、分类

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

        第二维度:监听的内容分:域对象的创建与销毁的、与对象的属性变化的、绑定到HttpSession域中的某个对象状态的

维度ServletContext域HttpSession域ServletRequest域

域对象的创建与销毁

ServletContextListenerHttpSessionListenerServletRequestListener
与对象的属性变化ServletContextAttributeListener
HttpSessionAttributeListene
ServletRequestAttributeListener
绑定到HttpSession域中某个对象的状态
-----
HttpSessionBindingListener
HttpSessionActivationListener
----

3、原理

        (1)  实现了特定接口的类为监听器,用来监听另一个java类的方法调用或者属性改变;

      (2)当被监听的对象发生了方法调用或者属性改变后,监听器的对应方法就会立即执行。

        监听器涉及到以下几个组成部分:

                1、事件源:被监听的对象,即:request、session、servletContext三大域对象。

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

                3、注册监听器:将监听器与事件源绑定,有两种注册方式:web.xml或@WebListener注解

                4、事件:域对象发生改变

                原理图:

                 

4、应用场景

        1.统计在线人数和在线用户
        2.系统启动时加载初始化信息
        3.统计网站访问量
        4.跟Spring结合

5、如何创建

        (1)定义一个普通类实现监听器接口

        (2)重写监听器接口方法

        (3)注册监听器:配置web.xml或@WebListener注解

二、监听域对象的创建与销毁

1、ServletContextListener

        ServletContextListener监听器:用来监听ServletContext域对象的创建和销毁。

        ServletContextListener对象代表全局唯一对象,每一个web工程会产生一个ServletContext对象,服务器启动时创建,服务器关闭时销毁。

        创建ServletContextListener监听器:

           (1)创建一个普通类,实现ServletContextListener接口,并重写内部的两个方法。

package com.ujiuye.com.ujiuye.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

/**
* 监听ServletContext的创建和销毁
* 1、创建一个普通的类实现特定的接口ServletContextListener
* 2、重写方法
* 3、注册监听器 web.xml和注解
*
* ServletContext随着tomcat的启动而创建,随着服务器的停止或者重启而销毁
*/
//@WebListener
public class Demo01Listener implements ServletContextListener {

    //初始化方法
    //当创建servletContext对象,就会被监听到,就会执行下面的方法,自动执行的
    @Override
    public void contextInitialized(ServletContextEvent sce) {

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

    //销毁方法
    @Override
    public void contextDestroyed(ServletContextEvent sce) {

        System.out.println("servletcontext销毁了创建了");
    }
}

        (2)注册监听器

                xml:

<!--注册监听器-->
    <listener>
        <listener-class>com.ujiuye.listener.Demo01Listener</listener-class>
    </listener>

                @WebListener:

                

        (3)测试监听器

                当tomcat服务器开启时执行被创建的方法contextInitialized,服务器被关闭执行被销毁的方法contextDestroyed。

                

                 

2、HttpSessionListener

        HttpSessionListener监听器:用来监听HttpSession域对象的创建和销毁。

        Session何时创建:Servlet中是request.getSession(),JSP页面中自带Session。

        Session何时销毁:非正常关闭服务器,Session过期,session.invalidate()

       创建HttpSessionListener监听器:

       (1)创建一个普通类,实现HttpSessionListener接口,并重写内部的两个方法。

package com.ujiuye.listener;

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

/**
* 测试request创建和销毁
*/
public class Demo02Listener implements ServletRequestListener {

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {

        System.out.println("request销毁了-------");
}

    @Override
    public void requestInitialized(ServletRequestEvent sre) {

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

        (2)注册监听器

                在MyHttpSessionListener监听器类的上方添加注解@WebListener

        (3)测试监听器

                测试Session对象的创建:创建一个jsp页面index.jsp

                直接访问index.jsp页面,由于Session时jsp的内置对象,意味着在访问index.jsp页面时创建了Session对象,因此会触发监听器的创建方法。

                测试Session对象的销毁:

                (1)可以在web.xml文件中配置一下session的有效期,让其在一分钟过期,当超过一分钟没有操作服务器页面就会触发销毁方法;

<session-config>
    <sesssion-timeout>1</sesssion-timeout>
</session-config>

                (2)执行session.invalidate()方法强制销毁session

3、ServletRequestListener

        ServletRequestListener监听器:用来监听ServletRequest域对象的创建和销毁。

        Request何时创建:请求发起时创建

        Request何时销毁:响应结束时销毁

        创建ServletRequestListener监听器:

        (1)创建一个普通类,实现ServletRequestListener接口,并重写内部的两个方法。

package com.ujiuye.listener;

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

/**
* 监听session创建和销毁
*
* 1、打开浏览器 访问项目 会话开始,就会创建一个session对象 session就创建了
*     关闭浏览器代表会话结束,在打开浏览器去访问项目,
*     只会创建一个新的session,原来的session只是找不到了,并没有立即销毁
* 2、session什么时候销毁
*     默认30分钟没有操作 tomcat/conf/web.xml session-timeout标签配置
*     可以在自己的web.xml进行配置
*     调用session.invalidate() 让当前session立即销毁
*/
//@WebListener
public class Demo03Listener implements HttpSessionListener {

    @Override
    public void sessionCreated(HttpSessionEvent se) {

        System.out.println("session创建");
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
    
        System.out.println("session销毁");
    }
}

     (2)注册监听器

        在MyServletRequestListener监听器类的上方添加注解@WebListener

     (3)测试监听器

        直接访问index.jsp,jsp有内置的request进行创建,请求结束销毁

三、监听域对象属性改变

ServletContext、HttpSession、ServletRequest中添加数据、修改数据、移除数据的监听器。

1、ServletContextAttributeListener

        ServletContextAttributeListener监听器:监听ServletContext中属性的变化。

2、HttpSessionAttributeListener

        HttpSessionAttributeListener监听器: 监听HttpSession中属性的变化。

3、ServletRequestAttributeListener

        ServletRequestAttributeListener 监听器:监听ServletRequest中属性的变化

        以下是此类监听器对域中属性进行不同操作时所触发的方法:
        (1)attributeAdded监听属性添加——当数据范围对象没有该属性,第一次添加时会自动触发调用执行
        (2)attributeRemoved 监听属性移除——从一个数据范围对象删除一个已经存在的属性时会自动触发执行。
        (3)attributeReplaced监听属性替换——当一个数据范围已经存在一个属性,向数据范围添加相同名称属性时自动触发替换方法。
        下面我们以HttpSessionAttributeListener监听器为例,其他两个同理
        (1)创建一个普通类,实现HttpSessionAttributeListener接口,并重写内部的三个方法。
package com.ujiuye.listener;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

/**
* 测试 session值的变化
*/
//@WebListener
public class Demo05Listener implements HttpSessionAttributeListener {

    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        //添加
        System.out.println("--------------------------------");
        System.out.println("向session中添加值");
       
        String name = se.getName();
        Object value = se.getValue();
        System.out.println(name);
        System.out.println(value);
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {
        //移除
        System.out.println("--------------------------------");
        System.out.println("向session中移除值");

        String name = se.getName();
        Object value = se.getValue();
        System.out.println(name);
        System.out.println(value);
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) {
        //替换
        System.out.println("--------------------------------");
        System.out.println("向session中替换值");
        //获取的是替换之前的键值对
        String name = se.getName();
        Object value = se.getValue();
        System.out.println(name);
        System.out.println(value);
    }
}

        (2)注册监听器

        监听器类的上方添加注解@webListener

        (3)测试监听器

        创建一个jsp页面session.jsp,在该页面中分别对session域做添加,移除,替换的操作,这样就会触发不同的方法执行

<%--
    Created by IntelliJ IDEA.
    User: why
    Date: 2022/10/28
    Time: 14:12
    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("age",17);
    session.setAttribute("age",77);
    session.removeAttribute("age");
%>
</body>
</html>

四、监听绑定到HttpSession域中某个对象状态

     此类监听器是Servlet中比较特殊的监听器,主要用来监听绑定到Session域中待定对象的状态。

        1.绑定:将java对象绑定到session中;

        2.解除绑定:将java对象从session中解除绑定;

        3.钝化:数据随Session对象序列化到一个存储设备中(硬盘);

        4.活化:数据随Session对象从一个存储设备中(硬盘)恢复到内存。

1、HttpSessionBindingListener

        实现HttpSessionBindingListener接口的java对象,可以感知自身被绑定到Session或者从Session中解除绑定。

案例:监听一个学生对象与Session绑定与解绑的状态

        (1)创建一个学生类student实现HttpSessionBindingListener接口,并重写内部两个方法。

package com.ujiuye.listener;

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

/**
*
* 第7个监听器
* 监听java对象从session中解绑或者绑定
*     指的是实现了HttpSessionBindingListener接口的对象 并不是所有的java对象
* 注意: 该监听器比较特殊,针对某一个特定的java对象,不需要去加上注解@WebListener
*/

public class Student implements HttpSessionBindingListener {
    private String name;
    private int age;
    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        //绑定
        System.out.println("-----------------------------------------");
        System.out.println("student对象绑定到session中");
        //获取绑定student对象简直对
        String name = event.getName();
        Object value = event.getValue();
        System.out.println(name);
        System.out.println(value);
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        //解绑
        System.out.println("-----------------------------------------");
        System.out.println("student对象从session中解绑了");
        //获取student对象解绑的键值对
        String name = event.getName();
        Object value = event.getValue();
        System.out.println(name);
        System.out.println(value);
    }


    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

        (2)测试:创建一个jsp页面,创建用户对象并分别绑定和移除Session进行测试

<%@ page import="com.ujiuye.listener.Student" %><%--
    Created by IntelliJ IDEA.
    User: why
    Date: 2022/10/28
    Time: 14:38
    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>
<%
    Student student = new Student("李华",48);
    //将student对象绑定到session中
    session.setAttribute("student",student);
    //从session中解绑
    session.removeAttribute("student");
%>
</body>
</html>

2、HttpSessionActivationListener

实现HttpSessionActivationListener接口的java对象,可以感知从内存被钝化到硬盘,从硬盘活化到内存中。

     钝化时机:服务器关闭或重启,指定时间内(长时间)不操作服务器。

     活化时机:服务器再次开启。

案例:监听一个老师对象的钝化与活化状态

        (1)创建一个老师类Teacher实现HttpSessionActivationListener接口,实现序列化接口,并重写内部两个方法。

package com.ujiuye.listener;

import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;
import java.io.Serializable;

/**
* 第八个监听器
*     1、监听session活化和钝化
*         钝化 将内存中的session存到本地磁盘
*         活化 将本地磁盘的session活跃到内存中
*     2、钝化时机: 关闭服务器、重启服务器
*     3、活化时机: 开启服务器
*     4、实现HttpSessionActivationListener接口
*     5、注意: 不需要加上注解@WebListener
*
* 测试步骤
* 1、要向session中添加Teacher对象
* 2、implements序列化接口Serializable
*
*/
public class Teacher implements HttpSessionActivationListener, Serializable{

    private String name;
    private int age;

    @Override
    public void sessionWillPassivate(HttpSessionEvent se) {
        //钝化9AAC29940B893CD0C785FA7A429440AF
        System.out.println("存储teacher对象的session钝化了");
        //获取session的id
        System.out.println(se.getSession().getId());
        System.out.println("------------------------------------");
    }

    @Override
    public void sessionDidActivate(HttpSessionEvent se) {
        //活化
        System.out.println("存储teacher对象的session活化了");
        System.out.println(se.getSession().getId());
        System.out.println("------------------------------------");
    }
   
    public Teacher() {
    }

    public Teacher(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

        (2)配置tomcat根目录下conf文件夹里的context.xml文件,其中directory="e:\test"表示自定义钝化文件的存储路径,maxIdleSwap="1"表示超过1分钟未操作服务器会自动钝化。

<Manager className="org.apache.catalina.session.PersistentManager"
saveOnRestart="true" maxIdleSwap="1">
<Store className="org.apache.catalina.session.FileStore"
directory="e:\test"/>
</Manager>

        (3)测试:创建一个listener_teacher.jsp页面,向Session中保存一个老师对象。

<%@ page import="com.ujiuye.listener.Teacher" %><%--
    Created by IntelliJ IDEA.
    User: why
    Date: 2022/10/28
    Time: 14:51
    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("teacher",new Teacher("黎老板",18));
%>
</body>
</html>

        再创建一个listener_teacher01.jsp页面获取活化后Session中的数据是否存在

<%--
    Created by IntelliJ IDEA.
    User: why
    Date: 2022/10/28
    Time: 15:01
    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>
    <%
        System.out.println(session.getAttribute("teacher"));
    %>
</body>
</html>

首先运行listener.jsp页面,向Session中存入老师信息,然后关闭服务器或通过设置maxidleSwap

="1"等待1分钟不操作服务器,都会触发钝化方法执行,同时在指定的路径e:\test下

会看到一个.session文件,将session中的数据钝化到该文件中。
        

         然后重新启动服务器,访问listener_teacher01.jsp,此时会触发活化方法将Session文件中的

数据读取到内存,会读取到数据。

        

3、特别注意

        实现这两个接口的类不需要有web.xml文件或注解中进行注册监听器,都有由Session自主完成的。

五、综合案例:统计网站在线人数

        思路分析:通过ServletContextListener监听,当Web应用上下文启动时,在ServletContext中

添加一个List集合,用来准备存放在线的用户名;然后可以通过HttpSessionAttributeListener监

听器,当用户登录成功,把用户名设置到Session中时,同时将用户名存放到ServletContext中的

List列表中;最后通过HttpSessionListener监听。

        online_login.jsp:

<%--
    Created by IntelliJ IDEA.
    User: why
    Date: 2022/10/28
    Time: 15: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>
<%--登陆页面--%>
<form action="onlineLoginServlet">
    用户名: <input type="text" name="username" value=""> <br>
    <input type="submit" value="登陆">
</form>
</body>
</html>

       online_main.jsp: 

<%--
    Created by IntelliJ IDEA.
    User: why
    Date: 2022/10/28
    Time: 15:44
    To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h2>显示在线用户名</h2>
<%--所有的用户名都在servletContext作用域中 application.getAttr(online)--%>
<div>
    在线人数:${applicationScope.online.size()}
</div>
    <c:forEach items="${applicationScope.online}" var="username">
        <li>${username }</li>
    </c:forEach>
</body>
</html>

        OnlineLoginServlet:

package com.ujiuye.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@WebServlet("/onlineLoginServlet")
public class OnlineLoginServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
        //设置字符集
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");
        //接收用户名
        String username = req.getParameter("username");
        //lists集合中就是已经注册过的用户名
        List<String> lists = new ArrayList<>();
        lists.add("张三1");
        lists.add("张三2");
        lists.add("张三3");
        lists.add("张三4");
        lists.add("李四1");
        lists.add("李四2");
        lists.add("李四3");
        lists.add("李四4");
        //判断用户是否登陆成功
        if(lists.contains(username)) {
            //将username放到session中
            req.getSession().setAttribute("username",username);
            //跳转到主页
            resp.sendRedirect("online_main.jsp");
        }else {
            System.out.println("登陆失败");
        }
    }
}

        OnlineListener:

package com.ujiuye.listener;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import java.util.ArrayList;
import java.util.List;

/**
* 统计在线人数
* 1、准备登陆页面
* 2、登陆成功之后,将用户名存储到session中
*     使用的是一个浏览器进行登陆,一直在一次会话中
*     第一次登陆 向session中添加键值对
*     第二次登陆 修改session中的键值对
*
* 3、实现ServletContextListener监听器
*     监听servletContext创建和销毁
*     tomcat启动时就会创建
* 4、实现HttpSessionAttributeListener监听器
*     监听session值的变化
*     添加值
*     替换值
* 5、在创建servletContext监听方法中
*     获取ServletContext对象,向其作用域添加键值对 <online,List集合>
*/

@WebListener
public class OnlineListener implements ServletContextListener,HttpSessionAttributeListener {
    private ServletContext sc;
    //创建servletContext
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        //获取servletContext
        sc = sce.getServletContext();
        //在servletContext作用域中添加一个键值对 <online,List<String>>
        List<String> online = new ArrayList<>();
        sc.setAttribute("online",online);
    }

    //session添加值
    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        //获取键值对的key
        String name = se.getName();
        //说明向session作用域添加的是用户名
        if("username".equals(name)) {
            //获取value
            String value = (String) se.getValue();
            //从sc中获取list集合
            List<String> online = (List<String>) sc.getAttribute("online");
            online.add(value);
//         //把online在放到sc作用域存着
            // sc.setAttribute("online",online);
        }
    }

    //session替换值
    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) {
        //获取键值对的key 替换之后的
        String name = se.getName();
        if("username".equals(name)) {
            //获取替换之后的用户名value
            //不能用getValue获取 因为获取的是之前的
            String username = (String) se.getSession().getAttribute("username");
            List<String> online = (List<String>) sc.getAttribute("online");
            online.add(username);
            // sc.setAttribute("online",online);
        }
    }
    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {
}
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    }
}

        

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

互联网底层民工

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值