Listener监听器,实现一个显示在线用户人数

Posted TheMagicalRainbowSea

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Listener监听器,实现一个显示在线用户人数相关的知识,希望对你有一定的参考价值。

Listener监听器,实现一个显示在线用户人数

每博一文案

关于后半身,脾气越温,福报越深。
师傅说:惜命最好的方式不是养生,而是管好自己的情绪。
坏毛病都是惯出来的,但好脾气都是磨出来的,与人生气,伤的是和气,与自己生气,伤的是身体。
佛说:人有五毒心,贪嗔痴慢疑,其中一时的嗔念起,百万叶障深,火烧功德林,脾气来了,福气就走了。
破得了偏执,才修得了善行。世上最亏本的事,就是用一时的冲动消耗了积累的福报。
既然你是对的,那你没必要发脾气。但如果你是错的,那你就没资格发脾气。
在感情的世界里,愤怒时的态度也是丈量幸福的尺度。
一个人生气是怎么对你,他平时就怎么爱你,在一起久了,性格会互补,爱得多的脾气,
会越来越好,被爱的会越来越霸道,总得有一个人原意低头迁就,所有的包容都来自深爱,
所有的好脾气都是不想失去,因为彼此心灵都明白,争执不是唯一的目的,磨合得更紧密,才是
最重要的主题。
爱,就是什么都介意,最后又什么都原谅脾气,人人都有,拿出来是本能,但压下去才是本事。
一个人成熟的标志就是变得不爱生气了,因为能让你在意的事情变少了,以前能冒犯到你的,
现在都变得无所谓了,以前无法容忍的,现在想想就作罢了。就像灰尘落在身上,轻轻拂去就好了。
不用蹲下懊恼,阻碍了前行的脚步。
你只有这一生,不能慷慨赠与没有意义的人,也不必为不值得的事,就轻易放弃开心的权利。
愿你此生尽兴,时辰善良,愿你所有的快乐都无需假装。
                                    ——————《一禅心灵庙语》

1. Listener 监听器的概述

Listener 监听器是Servlet规范中的一员。就像Filter一样。Filter也是Servlet规范中的一员。

在Servlet中,所有的监听器接口都是以Listener结尾。

Listener 监听器的作用:

  • 监听器实际上是Servlet规范留给我们javaweb程序员的特殊时机。特殊的时刻如果想执行这段代码,你需要想到使用对应的监听器。
  • 简单的说就是,当你的程序执行过程中满足的了/触发了某个事件,则该Listener 监听器中的方法就会被调用,并执行。比如:我们的Java中的 静态代码块 (当我们对于的 类加载到 内存当中时,该类当中的 静态代码块,就会被执行,并且只会仅仅只会执行一次。)如下:
package com.RainbowSea.listener;

public class Test 

    // 只要这个 Test 类(被加加载到了内存,这个事件)当中,该静态代码块就会被执行,并且只会仅仅只会执行一次。
    static 
        System.out.println("静态代码块被执行了");
    

    public static void main(String[] args) 
        Test test = new Test();  // 实例化,该对象的时候,就会将该 Test 类加载到内存当中,(该Test 加载
        // 到内存当中,该事件触发,执行其中 Test 类当中定义的 静态代码块。 )
    




  • 类似的,如果你对 javaScript 有一个了解的话,那么你就可以,更好的理解这里所受的 事件 这个元素了。因为JavaScript就是一个基于 触发事件的执行的语言。

2. Listener 监听器在 Servlet 的体系结构

  • 概念:Listener表示监听器,是JavaWeb三大组件(Servlet、Filter、Listener)之一。
  • 监听器可以监听就是在application,session,request三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件
  • Listener分类:JavaWeb中提供了8个监听器
监听器分类 监听器名称 作用
ServletContext监听 ServletContextListener 用于对ServletContext对象进行监听( 创建、销毁)
ServletContextAttributeListener 对ServletContext对象中属性的监听( 增删改属性)
Session监听 HttpSessionListener 对Session对象的整体状态的监听(创建、销毁)
HttpSessionBindingListener 监听对象与Session的绑定和解除
HttpSessionActivationListener 对Session数据的钝化和活化的监听
Request监听 ServletRequestListener 对Request对象进行监听(创建、销毁)
ServletContextAttributeListener 对Request对象中属性的监听(增删改属性)

下面的是基于 Tomcat 10 的环境下的:Servlet规范中提供的监听器 ?

  • jakarta.servlet包下:
    • ServletContextListener
    • ServletContextAttributeListener
    • ServletRequestListener
    • ServletRequestAttributeListener
  • jakarta.servlet.http包下:
    • HttpSessionListener
    • HttpSessionAttributeListener
      • 该监听器需要使用 @WebListener注解进行标注。
      • 该监听器监听的是什么?是session域中数据的变化。只要数据变化,则执行相应的方法。主要监测点在session域对象上。
    • HttpSessionBindingListener
      • 该监听器不需要使用@WebListener进行标注。
      • 假设User类实现了该监听器,那么User对象在被放入session的时候触发bind事件,User对象从session中删除的时候,触发unbind事件。
      • 假设Customer类没有实现该监听器,那么Customer对象放入session或者从session删除的时候,不会触发bind和unbind事件。
      • 文章后面,会对其详细说明。
    • HttpSessionIdListener
      • session的id发生改变的时候,监听器中的唯一一个方法就会被调用。
    • HttpSessionActivationListener
      • 监听session对象的钝化和活化的。
      • 钝化:session对象从内存存储到硬盘文件。
      • 活化:从硬盘文件把session恢复到内存。

3. 编写一个 Listener 监听器的过程

3.1 ServletContextListener

除了Servlet和Filter外,JavaEE的Servlet规范还提供了第三种组件:Listener。

Listener顾名思义就是监听器,有好几种Listener,其中最常用的是ServletContextListener,我们编写一个实现了ServletContextListener接口。在编写之前我们先回顾一下: ServletContext 的作用:一个Web服务器可以运行一个或多个WebApp,对于每个WebApp,Web服务器都会为其创建一个全局唯一的ServletContext实例(因为一个 webApp 就只有一个 名为 web.xml 的配置文件),我们在AppListener里面编写的两个回调方法实际上对应的就是ServletContext实例的创建和销毁:ServletContext是一个WebApp运行期的全局唯一实例,可用于设置和共享配置信息。

ServletContextListener 监听器作用是:要监听的是 ServletContext的对象的状态 其中的两个方法 contextInitialized() 监听 ServletContext 的创建,contextDestroyed()监听 ServletContext 的销毁的 ,这个创建和销毁的状态事情。

其中两个方法是作用:

注意一点:就是:监听器中的方法不需要程序员手动调用,是发生某个特殊事件(触发)之后,被服务器调用。

/**
     ** Notification that the web application initialization process is starting.
     * All ServletContextListeners are notified of context initialization before
     * any filter or servlet in the web application is initialized.
     * The default implementation is a NO-OP.
     * @param sce Information about the ServletContext that was initialized
     */
    public default void contextInitialized(ServletContextEvent sce) 
        // 该方法的作用是:这个方法是在ServletContext 对象被创建的时候调用
        
    
/**
     ** Notification that the servlet context is about to be shut down. All
     * servlets and filters have been destroyed before any
     * ServletContextListeners are notified of context destruction.
     * The default implementation is a NO-OP.
     * @param sce Information about the ServletContext that was destroyed
     */
    public default void contextDestroyed(ServletContextEvent sce) 
        // 这个方法是ServletContext 对象被销毁的时候执行
    

上述两个方法是举例说明:

编写一个 Servlet 创建 ServletContext 应用域对象,触发创建 ServletContext 事件

package com.RainbowSea.listener;

import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;


@WebServlet("/test")
public class MyServlet extends HttpServlet 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException 

        // 创建 ServetContext 应用域对象
        ServletContext servletContext = request.getServletContext();

    


创建 TestServletContextListener 类 实现 ServletContextListener 接口,用于事件的触发,服务器调用其中的方法()

package com.RainbowSea.listener;

import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;



/**
 * ServletContextListener 是用来监视 ServletContext 应用域的状态的监听器
 */

public class TestServletContextListener implements ServletContextListener 

    @Override
    public void contextInitialized(ServletContextEvent sce) 
        // 现在这个特殊的时刻写代码,你写就是了,他会被服务器自动调用了
        // 这个方法是在ServletContext 对象被创建的时候调用
        System.out.println("ServletContext  应用域对象创建了");

    

    @Override
    public void contextDestroyed(ServletContextEvent sce) 
        //这个方法是 在 ServletContext 对象被销毁的时候调用的
        System.out.println("ServletContext 应用域对被销毁了");
    


编写该 ServletContextListener 的配置信息,在 web.xml 当中,如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0">
    
    <listener>
        <listener-class>com.RainbowSea.listener.TestServletContextListener</listener-class>
    </listener>
    
</web-app>

也可以使用该注解@WebListener() 注解的方式,需要注意的是:该注解不需要赋值属性,使用默认的就可以了。

运行结果:


4. 其他Listener 监听器的使用:

介绍一些其他常用的:Listener 监听器的使用:

4.1 ServletRequestListener

ServletRequestListener 监听器的作用:监听的是 Request 请求域的状态,主要为:监听 ServletRequestListener() 销毁,requestInitialized() 创建。

ServletRequestListener 中的两个 default 默认方法的说明:

/**
     * The request is about to go out of scope of the web application.
     * The default implementation is a NO-OP.
     * @param sre Information about the request
     */
    public default void requestDestroyed (ServletRequestEvent sre) 
        // 该方法表示:当 request 请求对象销毁的时候执行。
    
/**
     * The request is about to come into scope of the web application.
     * The default implementation is a NO-OP.
     * @param sre Information about the request
     */
    public default void requestInitialized (ServletRequestEvent sre) 
        // 表方法表示:当 request 请求对象创建的时候,执行
    

举例:

package com.RainbowSea.listener;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;


@WebServlet("/test")
public class MyServlet extends HttpServlet 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException 


    


package com.RainbowSea.listener;

import jakarta.servlet.ServletRequestEvent;
import jakarta.servlet.ServletRequestListener;
import jakarta.servlet.annotation.WebListener;


/**
 * ServletRequestLister 是用来监听 Request 请求域对象的销毁和创建两种状态的。
 */

@WebListener // 使用注解的方式,不要赋值,使用 默认值
public class TestServletRequestListener implements ServletRequestListener 

    // 该方法当 request 请求域对象要被销毁的时候,tomcat 服务器调用该方法
    @Override
    public void requestDestroyed(ServletRequestEvent sre) 
        System.out.println("request 请求对象被销毁了");
    

    // 该方法当 request 请求域对象创建的时候,Tomcat 服务器调用该方法
    @Override
    public void requestInitialized(ServletRequestEvent sre) 
        System.out.println("request 请求对象创建了");
    


运行测试结果:


ng)

4.2 HttpSessionListener

**HttpSessionListener 监听的作用:是监听 session 会话域对象的创建和销毁的状态:sessionCreated()监视 Session 的创建,sessionDestroyed() 监视销毁。 **

其中两个方法的详细说明:

/**
     * Notification that a session was created.
     * The default implementation is a NO-OP.
     *
     * @param se
     *            the notification event
     */
    public default void sessionCreated(HttpSessionEvent se) 
        // 该方法当 session 会话域对象创建的时候,被 Tomcat 服务器调用并执行
    
/**
     * Notification that a session is about to be invalidated.
     * The default implementation is a NO-OP.
     *
     * @param se
     *            the notification event
     */
    public default void sessionDestroyed(HttpSessionEvent se) 
        // 该方法当 session 会话域对象销毁的时候,被 Tomcat 服务器调用并执行
    

举例1验证:

创建一个 MyServlet 类,该类当中创建 Session 会话域对象,触发 HttpSessionListener 中 session 销毁事件,让服务器调用其中的方法,同时编写调用 session 中的销毁的方法,触发销毁是事件。

package com.RainbowSea.listener;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import java.io.IOException;


@WebServlet("/test")
public class MyServlet extends HttpServlet 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException 

        // session 会话域对象是来自服务器的,所有是通过 request 请求获取到的
        // 该方法如果服务器中没有 session 会话域对象,则自动创建
        HttpSession session = request.getSession();

        if (session != null) 
            // session 销毁
            session.invalidate();
        

    


package com.RainbowSea.listener;

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


/**
 * HttpSessionListener 是用来监视 session 会话域对象的创建和销毁的状态的。
 */

@WebListener // 使用注解配置,不需要赋值使用默认的值
public class TestHttpSessionListener implements HttpSessionListener 
    @Override
    public void sessionCreated(HttpSessionEvent se) 
       // 该方法 session 创建的时候,被Tomcat 服务器调用并执行
        System.out.println("session 会话域对象创建了");
    

    @Override
    public void sessionDestroyed(HttpSessionEvent se) 
        System.out.println("session 会话域对象被销毁了");
    


测试效果:

举例2 我们启用 JSP中九大内置对象中的之一的 session 对象


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<a href="<%=request.getContextPath()%>/test"> session 会话销毁</a>

</body>
</html>

package com.RainbowSea.listener;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import java.io.IOException;


@WebServlet("/test")
public class MyServlet extends HttpServlet 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException 

        // session 会话域对象是来自服务器的,所有是通过 request 请求获取到的
        // 该方法如果服务器中没有 session 会话域对象,不会自动创建
        HttpSession session = request.getSession(false);

        if (session != null)   // 防止 null 引用
            // session 销毁
            session.invalidate();
        

    


package com.RainbowSea.listener;

import jakarta.servlet.ServletRequestEvent;
import jakarta.servlet.ServletRequestListener;
import jakarta.servlet.annotation.WebListener;


/**
 * ServletRequestLister 是用来监听 Request 请求域对象的销毁和创建两种状态的。
 */

//@WebListener // 使用注解的方式,不要赋值,使用 默认值
public class TestServletRequestListener implements ServletRequestListener 

    // 该方法当 request 请求域对象要被销毁的时候,tomcat 服务器调用该方法
    @Override
    public void requestDestroyed(ServletRequestEvent sre) 
        System.out.println("request 请求对象被销毁了");
    

    // 该方法当 request 请求域对象创建的时候,Tomcat 服务器调用该方法
    @Override
    public void requestInitialized(ServletRequestEvent sre) 
        System.out.println("request 请求对象创建了");
    


测试效果:

4.3 HttpSessionAttributeListener

HttpSessionAttributeListener 的监听器的作用是:监视 向Session 会话域当中 添加,修改,移除数据的状态的事件的监听。

同理的其中对应的这些

  • jakarta.servlet.ServletContextAttributeListener 也是一样的其作用是:向 ServletContext 应用域当中添加,修改,移除数据的状态的事件的监听。

  • jakarta.servlet.ServletRequestAttributeListener也是一样的其作用是:向 Request 请求域当中添加,修改,移除数据的状态的事件的监听。

这三者的接口的结构是一样的,其方法的作用也是一样的,使用的方法也是一样的,这里就不多赘述了,这里就仅仅对其中的 jakarta.servlet.http.HttpSessionAttributeListener 作演示,另外两个的使用大致都是和它是一样的。

这里下面是对jakarta.servlet.http.HttpSessionAttributeListener接口中的 方法的说明:

/**
     * Notification that an attribute has been added to a session. Called after
     * the attribute is added.
     * The default implementation is a NO-OP.
     *
     * @param se Information about the added attribute
     */
    public default void attributeAdded(HttpSessionBindingEvent se) 
      // 向Session 会话域当中添加存储数据的时候,以下方法被服务器调用执行
    
/**
     * Notification that an attribute has been removed from a session. Called
     * after the attribute is removed.
     * The default implementation is a NO-OP.
     *
     * @param se Information about the removed attribute
     */
    public default void attributeRemoved(HttpSessionBindingEvent se) 
        // 将Session 会话域当中的数据删除的时候,以下方法被服务器调用执行了
    
/**
     * Notification that an attribute has been replaced in a session. Called
     * after the attribute is replaced.
     * The default implementation is a NO-OP.
     *
     * @param se Information about the replaced attribute
     */
    public default void attributeReplaced(HttpSessionBindingEvent se) 
         // 将Session 会话当中某个数据被替换修改了,以下方法被服务器调用执行
    

举例验证:

package com.RainbowSea.listener;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import java.io.IOException;


@WebServlet("/test")
public class MyServlet extends HttpServlet 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException 

        // session 会话域对象是来自服务器的,所有是通过 request 请求获取到的
        // 该方法如果服务器中没有 session 会话域对象,则会自动创建
        HttpSession session = request.getSession();

        // 向 session 会话域当中添加数据
        session.setAttribute("test",001);

        // 向 session 会话域当中替换数据
        session.setAttribute("test",100);

        // session 会话域当中移除数据
        session.removeAttribute("test");
    


package com.RainbowSea.listener;

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


/**
 * HttpSessionAttributeListener 监视 session 会话域当中的数据的添加,移除,修改(替换) 事件的监听
 */
@WebListener // 使用注解:使用注解当中的默认值,不需要赋值
public class TestHttpSessionAttributeListener implements HttpSessionAttributeListener 
    @Override
    public void attributeAdded(HttpSessionBindingEvent se) 
        // 向 session 会话域当中添加数据时,该方法被服务器调用,并执行了
        System.out.println("session add 添加数据了");
    

    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) 
        // 向 session 会话域当中移除数据时,该方法被服务器调用,并执行了
        System.out.println("session removed 移除数据了");
    

    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) 
        // 向 session 会话域当中的数据 被 替换时,该方法被服务器调用,并执行了
        System.out.println("session replaced 替换数据了");
    


Debugger 断点测试验证:

4.4 HttpSessionBindingListener

HttpSessionBindingListener 监视器的作用:是 对应一个 Javabeen 对象的实现了implements HttpSessioniBingingListener 接口的添加到 Session 会话域当中的。

  • 该监听器不需要使用@WebListener进行标注。
  • 假设User类实现了该监听器,那么User对象在被放入session的时候触发绑定 bind事件,User对象从session中删除的时候,触发解绑 unbind事件。
  • 假设Customer类没有实现该监听器,那么Customer对象放入session或者从session删除的时候,不会触发bind和unbind事件

如下是关于:HttpSessionBingingListener 两个方法的详细说明:

 /**
     * Notifies the object that it is being bound to a session and identifies
     * the session.
     * The default implementation is a NO-OP.
     *
     * @param event
     *            the event that identifies the session
     * @see #valueUnbound
     */
    public default void valueBound(HttpSessionBindingEvent event) 
        // 向 session 会话域当中添加 : 对象(该对象实现了 HttpSessionBingingListener) 的 javabeen 绑定数据 。
    
/**
     * Notifies the object that it is being unbound from a session and
     * identifies the session.
     * The default implementation is a NO-OP.
     *
     * @param event
     *            the event that identifies the session
     * @see #valueBound
     */
    public default void valueUnbound(HttpSessionBindingEvent event) 
        // 将 session 会话域当中的移除: (该对象实现了 HttpSessionBingingListener) 的 javabeen ) 解绑数据
    

**JavaBeen 类实现 HttpSessionBindingListener **

package com.RainbowSea.been;

import jakarta.servlet.http.HttpSessionBindingEvent;
import jakarta.servlet.http.HttpSessionBindingListener;

import java.util.Objects;

public class User implements HttpSessionBindingListener 
    private String name ;
    private String password;


    @Override
    public void valueBound(HttpSessionBindingEvent event) 
        // 将该 User 实现了 HttpSessionBindingListener 接口的 javabeen 添加到 session 会话域调用;
        System.out.println("Session  绑定 JavaBeen 数据");
    

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) 
        // 将该 User 实现了 HttpSessionBindingListener 接口的 javabeen的从 session 会话域当中移除被调用
        System.out.println("Session 解绑 JaveBeen 数据");
    

    public User() 
    


    public User(String name, String password) 
        this.name = name;
        this.password = password;
    


    public String getName() 
        return name;
    

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

    public String getPassword() 
        return password;
    

    public void setPassword(String password) 
        this.password = password;
    


    @Override
    public boolean equals(Object o) 
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User user = (User) o;
        return Objects.equals(getName(), user.getName()) && Objects.equals(getPassword(), user.getPassword());
    

    @Override
    public int hashCode() 
        return Objects.hash(getName(), getPassword());
    


    @Override
    public String toString() 
        return "User" +
                "name=\'" + name + \'\\\'\' +
                ", password=\'" + password + \'\\\'\' +
                \'\';
    


package com.RainbowSea.listener;

import com.RainbowSea.been.User;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import java.io.IOException;


@WebServlet("/user")
public class TestHttpSessionBingingListener extends HttpServlet 

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException 

        // 获取到 session 应用域对象
        HttpSession session = request.getSession();
        User  user = new User("Jack","123");

        // 向 session 应用域当中添加  实现了 HttpSessionBindingListener 的 JavaBeen
        session.setAttribute("user",user);

        //  将 session 应用域当中数据移除, session 当中的 javabeen
        session.removeAttribute("user");

    


Debugger 测试:


举例测试:如果我们向 Session 添加的数据JavaBeen 并没有实现 HttpSessionBindingListener 这个接口,就算我们添加了 session是不会调用其中 HttpSessionBindingListener 接口中的 valueBound ()绑定 和 valueUnBound() 解绑的

package com.RainbowSea.been;

import jakarta.servlet.http.HttpSessionBindingEvent;
import jakarta.servlet.http.HttpSessionBindingListener;

import java.util.Objects;

public class User 
    private String name ;
    private String password;



    public User() 
    


    public User(String name, String password) 
        this.name = name;
        this.password = password;
    


    public String getName() 
        return name;
    

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

    public String getPassword() 
        return password;
    

    public void setPassword(String password) 
        this.password = password;
    


    @Override
    public boolean equals(Object o) 
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User user = (User) o;
        return Objects.equals(getName(), user.getName()) && Objects.equals(getPassword(), user.getPassword());
    

    @Override
    public int hashCode() 
        return Objects.hash(getName(), getPassword());
    


    @Override
    public String toString() 
        return "User" +
                "name=\'" + name + \'\\\'\' +
                ", password=\'" + password + \'\\\'\' +
                \'\';
    


package com.RainbowSea.listener;

import com.RainbowSea.been.User;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import java.io.IOException;


@WebServlet("/user")
public class TestHttpSessionBingingListener extends HttpServlet 

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException 

        // 获取到 session 应用域对象
        HttpSession session = request.getSession();
        User  user = new User("Jack","123");

        // 向 session 应用域当中添加  实现了 HttpSessionBindingListener 的 JavaBeen
        session.setAttribute("user",user);

        //  将 session 应用域当中数据移除, session 当中的 javabeen
        session.removeAttribute("user");

    


HttpSessionBindingListener 与 HttpSessionAttrlbuteListener 的区别:

  • HttpSessionBindingListener 是不需要注解的 也是不需要配置 web.xml 的
  • HttpSessionAttrlbuteListener 是需要注解/ 配置 web.xml 文件的
  • 两者都是对应 Session 会话域的监听的。
  • HttpSessionBindingListener 中的 如果我们向 Session 添加的数据JavaBeen 并没有实现 HttpSessionBindingListener 这个接口,就算我们添加了 session是不会调用其中 HttpSessionBindingListener 接口中的 valueBound ()绑定 和 valueUnBound() 解绑的。
  • HttpSessionAttrlbuteListener 只要是向 session 添加,删除,修改(替换) 数据服务器都的调用其中的事件中的方法。

5. Listener监听器的案例:实现一个显示登录的在线人数

为了有助于 大家阅读如下内容了解该 oa ,大家可以移步至:

监听器的应用

监听器在JavaWeb开发中用得比较多,下面说一下监听器(Listener)在开发中的常见应用

一、统计当前在线人数

  在JavaWeb应用开发中,有时候我们需要统计当前在线的用户数,此时就可以使用监听器技术来实现这个功能了。

技术分享图片
 1 package me.gacl.web.listener;
 2 
 3 import javax.servlet.ServletContext;
 4 import javax.servlet.http.HttpSessionEvent;
 5 import javax.servlet.http.HttpSessionListener;
 6 
 7 /**
 8 * @ClassName: OnLineCountListener
 9 * @Description: 统计当前在线用户个数
10 * @author: 孤傲苍狼
11 * @date: 2014-9-10 下午10:01:26
12 *
13 */ 
14 public class OnLineCountListener implements HttpSessionListener {
15 
16     @Override
17     public void sessionCreated(HttpSessionEvent se) {
18         ServletContext context = se.getSession().getServletContext();
19         Integer onLineCount = (Integer) context.getAttribute("onLineCount");
20         if(onLineCount==null){
21             context.setAttribute("onLineCount", 1);
22         }else{
23             onLineCount++;
24             context.setAttribute("onLineCount", onLineCount);
25         }
26     }
27 
28     @Override
29     public void sessionDestroyed(HttpSessionEvent se) {
30         ServletContext context = se.getSession().getServletContext();
31         Integer onLineCount = (Integer) context.getAttribute("onLineCount");
32         if(onLineCount==null){
33             context.setAttribute("onLineCount", 1);
34         }else{
35             onLineCount--;
36             context.setAttribute("onLineCount", onLineCount);
37         }
38     }
39 }
技术分享图片

二、自定义Session扫描器

  当一个Web应用创建的Session很多时,为了避免Session占用太多的内存,我们可以选择手动将这些内存中的session销毁,那么此时也可以借助监听器技术来实现。

技术分享图片
  1 package me.gacl.web.listener;
  2 
  3 import java.util.Collections;
  4 import java.util.LinkedList;
  5 import java.util.List;
  6 import java.util.ListIterator;
  7 import java.util.Timer;
  8 import java.util.TimerTask;
  9 import javax.servlet.ServletContextEvent;
 10 import javax.servlet.ServletContextListener;
 11 import javax.servlet.http.HttpSession;
 12 import javax.servlet.http.HttpSessionEvent;
 13 import javax.servlet.http.HttpSessionListener;
 14 
 15 /**
 16 * @ClassName: SessionScanerListener
 17 * @Description: 自定义session扫描器
 18 * @author: 孤傲苍狼
 19 * @date: 2014-9-10 下午10:16:42
 20 * 
 21 */ 
 22 public class SessionScanerListener implements HttpSessionListener,ServletContextListener {
 23 
 24     /**
 25     * @Field: list
 26     *          定义一个集合存储服务器创建的HttpSession
 27     *        LinkedList不是一个线程安全的集合
 28     */ 
 29     /**
 30      * private List<HttpSession> list = new LinkedList<HttpSession>();
 31      * 这样写涉及到线程安全问题,SessionScanerListener对象在内存中只有一个
 32      * sessionCreated可能会被多个人同时调用,
 33      * 当有多个人并发访问站点时,服务器同时为这些并发访问的人创建session
 34      * 那么sessionCreated方法在某一时刻内会被几个线程同时调用,几个线程并发调用sessionCreated方法
 35      * sessionCreated方法的内部处理是往一个集合中添加创建好的session,那么在加session的时候就会
 36      * 涉及到几个Session同时抢夺集合中一个位置的情况,所以往集合中添加session时,一定要保证集合是线程安全的才行
 37      * 如何把一个集合做成线程安全的集合呢?
 38      * 可以使用使用 Collections.synchronizedList(List<T> list)方法将不是线程安全的list集合包装线程安全的list集合
 39      */
 40     //使用 Collections.synchronizedList(List<T> list)方法将LinkedList包装成一个线程安全的集合
 41     private List<HttpSession> list = Collections.synchronizedList(new LinkedList<HttpSession>());
 42     //定义一个对象,让这个对象充当一把锁,用这把锁来保证往list集合添加的新的session和遍历list集合中的session这两个操作达到同步
 43     private Object lock = new Object();
 44             
 45     @Override
 46     public void sessionCreated(HttpSessionEvent se) {
 47         System.out.println("session被创建了!!");
 48         HttpSession session = se.getSession();
 49         
 50         synchronized (lock){
 51             /**
 52              *将该操作加锁进行锁定,当有一个thread-1(线程1)在调用这段代码时,会先拿到lock这把锁,然后往集合中添加session,
 53              *在添加session的这个过程中假设有另外一个thread-2(线程2)来访问了,thread-2可能是执行定时器任务的,
 54              *当thread-2要调用run方法遍历list集合中的session时,结果发现遍历list集合中的session的那段代码被锁住了,
 55              *而这把锁正在被往集合中添加session的那个thread-1占用着,因此thread-2只能等待thread-1操作完成之后才能够进行操作
 56              *当thread-1添加完session之后,就把lock放开了,此时thread-2拿到lock,就可以执行遍历list集合中的session的那段代码了
 57              *通过这把锁就保证了往集合中添加session和变量集合中的session这两步操作不能同时进行,必须按照先来后到的顺序来进行。
 58              */
 59             list.add(session);
 60         }
 61     }
 62 
 63     @Override
 64     public void sessionDestroyed(HttpSessionEvent se) {
 65         System.out.println("session被销毁了了!!");
 66     }
 67 
 68     /* Web应用启动时触发这个事件
 69      * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
 70      */
 71     @Override
 72     public void contextInitialized(ServletContextEvent sce) {
 73         System.out.println("web应用初始化");
 74         //创建定时器
 75         Timer timer = new Timer();
 76         //每隔30秒就定时执行任务
 77         timer.schedule(new MyTask(list,lock), 0, 1000*30);
 78     }
 79 
 80     @Override
 81     public void contextDestroyed(ServletContextEvent sce) {
 82         System.out.println("web应用关闭");
 83     }
 84 }
 85 
 86 /**
 87 * @ClassName: MyTask
 88 * @Description:定时器要定时执行的任务
 89 * @author: 孤傲苍狼
 90 * @date: 2014-9-11 上午12:02:36
 91 *
 92 */ 
 93 class MyTask extends TimerTask {
 94         
 95     //存储HttpSession的list集合
 96     private List<HttpSession> list;
 97     //存储传递过来的锁
 98     private Object lock;
 99     public MyTask(List<HttpSession> list,Object lock){
100         this.list = list;
101         this.lock = lock;
102     }
103     /* run方法指明了任务要做的事情
104      * @see java.util.TimerTask#run()
105      */
106     @Override
107     public void run() {
108             //将该操作加锁进行锁定
109         synchronized (lock) {
110             System.out.println("定时器执行!!");
111             ListIterator<HttpSession> it = list.listIterator();
112             /**
113              * 迭代list集合中的session,在迭代list集合中的session的过程中可能有别的用户来访问,
114              * 用户一访问,服务器就会为该用户创建一个session,此时就会调用sessionCreated往list集合中添加新的session,
115              * 然而定时器在定时执行扫描遍历list集合中的session时是无法知道正在遍历的list集合又添加的新的session进来了,
116              * 这样就导致了往list集合添加的新的session和遍历list集合中的session这两个操作无法达到同步
117              * 那么解决的办法就是把"list.add(session)和while(it.hasNext()){//迭代list集合}"这两段代码做成同步,
118              * 保证当有一个线程在访问"list.add(session)"这段代码时,另一个线程就不能访问"while(it.hasNext()){//迭代list集合}"这段代码
119              * 为了能够将这两段不相干的代码做成同步,只能定义一把锁(Object lock),然后给这两步操作加上同一把锁,
120              * 用这把锁来保证往list集合添加的新的session和遍历list集合中的session这两个操作达到同步
121              * 当在执行往list集合添加的新的session操作时,就必须等添加完成之后才能够对list集合进行迭代操作,
122              * 当在执行对list集合进行迭代操作时,那么必须等到迭代操作结束之后才能够往往list集合添加的新的session
123              */
124             while(it.hasNext()){
125                 HttpSession session = (HttpSession) it.next();
126                 /**
127                  * 如果当前时间-session的最后访问时间>1000*15(15秒)
128                  * session.getLastAccessedTime()获取session的最后访问时间
129                  */
130                 if(System.currentTimeMillis()-session.getLastAccessedTime()>1000*30){
131                     //手动销毁session
132                     session.invalidate();
133                     //移除集合中已经被销毁的session
134                     it.remove();
135                 }
136             }
137         }
138     }
139 }
技术分享图片

 以上就是监听器的两个简单应用场景。

以上是关于Listener监听器,实现一个显示在线用户人数的主要内容,如果未能解决你的问题,请参考以下文章

利用Listener实现网站累积访问人数最大同时在线人数当前登录用户数的记录

通过session统计当前在线人数

servlet监听器Listener(理论+例子)

JavaWeb学习 (二十七)————监听器(Listener)在开发中的应用

监听器的应用

Listener学习