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 ,大家可以移步至: B/S 结构系统的 缓存机制(Cookie) 以及基于 cookie 机制实现 oa 十天免登录的功能_ChinaRainbowSea的博客-CSDN博客

  • 思考一个业务场景:

    • 请编写一个功能,记录该网站实时的在线用户的个数。
    • 我们可以通过服务器端有没有分配session对象,因为一个session代表了一个用户。有一个session就代表有一个用户。如果你采用这种逻辑去实现的话,session有多少个,在线用户就有多少个。这种方式的话:HttpSessionListener够用了。session对象只要新建,则count++,然后将count存储到ServletContext域当中,在页面展示在线人数即可。
    • 业务发生改变了,只统计登录的用户的在线数量,这个该怎么办?
      • session.setAttribute("user", userObj);
      • 用户登录的标志是什么?session中曾经存储过User类型的对象。那么这个时候可以让User类型的对象实现HttpSessionBindingListener监听器,只要User类型对象存储到session域中,则count++,然后将count++存储到ServletContext对象中。页面展示在线人数即可。
  • 实现oa项目中当前登录在线的人数。

    • 什么代表着用户登录了?

      • session.setAttribute("user", userObj); User类型的对象只要往session中存储过,表示有新用户登录。
    • 什么代表着用户退出了?
      • session.removeAttribute("user"); User类型的对象从session域中移除了。
      • 或者有可能是session销毁了。(session超时)

User 的 JavaBeen 核心代码:

public void valueBound(HttpSessionBindingEvent event) {
// 用户登录了
// Dept 类型的对象向session 中存放了
// 获取到ServletContext 应用域对象,一个应用只要一个
ServletContext application = event.getSession().getServletContext(); Object onlinecount = application.getAttribute("onlinecount"); // 用户第一次登录,一定是 onlinecout 是不存在的
if(onlinecount == null) {
// 向ServletContext 应用域对象,添加该用户,第一个用户的登录
application.setAttribute("onlinecount", 1);
} else {
// 第二个用户的登录,就存在这个 onlinecount 应用域的对象信息了
int count = (Integer) onlinecount; // 将从 ServletContext 应用存储的信息用户的个数取出 count++;
application.setAttribute("onlinecount",count); // 修改,增加用户个数信息。
} } @Override
public void valueUnbound(HttpSessionBindingEvent event) {
// 用户退出了
// Dept 类型的对象从 session 对象中删除了。
// 存储在 ServletContext中的 onlinecount key 对应的 value 用户个数 -- // 1. 获取到存储 ServletContext中的用户个数
ServletContext servletContext = event.getSession().getServletContext();
Object onlinecount = servletContext.getAttribute("onlinecount"); int count = (Integer) onlinecount;
count --; servletContext.setAttribute("onlinecount",count); }

完整核心代码:

package com.RainbowSea.bean;

import jakarta.servlet.ServletContext;
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; public User() {
} public User(String name, String password) {
this.name = name;
this.password = password;
} @Override
public void valueBound(HttpSessionBindingEvent event) {
// 用户登录了
// Dept 类型的对象向session 中存放了
// 获取到ServletContext 应用域对象,一个应用只要一个
ServletContext application = event.getSession().getServletContext(); Object onlinecount = application.getAttribute("onlinecount"); // 用户第一次登录,一定是 onlinecout 是不存在的
if(onlinecount == null) {
// 向ServletContext 应用域对象,添加该用户,第一个用户的登录
application.setAttribute("onlinecount", 1);
} else {
// 第二个用户的登录,就存在这个 onlinecount 应用域的对象信息了
int count = (Integer) onlinecount; // 将从 ServletContext 应用存储的信息用户的个数取出 count++;
application.setAttribute("onlinecount",count); // 修改,增加用户个数信息。
} } @Override
public void valueUnbound(HttpSessionBindingEvent event) {
// 用户退出了
// Dept 类型的对象从 session 对象中删除了。
// 存储在 ServletContext中的 onlinecount key 对应的 value 用户个数 -- // 1. 获取到存储 ServletContext中的用户个数
ServletContext servletContext = event.getSession().getServletContext();
Object onlinecount = servletContext.getAttribute("onlinecount"); int count = (Integer) onlinecount;
count --; servletContext.setAttribute("onlinecount",count); } 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 + '\'' +
'}';
}
}

对应的JSP 列表用户,获取到存储到 Context 应用域当中在线用户的个数。

<h1 align="center">部门列表</h1>
<%--显示用户在线的个数,其信息存储在了 ServletContext()应用域当中了--%>
<h3>在线人数: <%=request.getServletContext().getAttribute("onlinecount")%></h3>
<%--注意这里使用的是 jsp 内置的 session 对象所以不可以,不要把 session 禁用了。--%>

完成的 JSP 代码:

<%@ page import="com.RainbowSea.bean.Dept" %>
<%@ page import="java.util.List" %> <%--这是导包在jSP 当中,并翻译为import导入对于的报--%>
<%@ page import="java.lang.Integer" %>
<%@page contentType="text/html; charset=utf-8" %> <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>部门列表页面</title>
</head>
<script type="text/javascript">
function del(dno) {
// 弹出确认框,用户点击确定,返回true,点击取消返回false
var ok = window.confirm("亲,删了不可恢复哦!");
if (ok) {
// 发送请求进行删除数据的操作
// 在js代码当中如何发送请求给服务器
//
// document.location.href="请求路径"
// document.location = "请求路径"
// window.location.href = "请求路径"
// window.location = "请求路径" // 注意是根据所传的部门编号删除数据的
document.location.href = "<%=request.getContextPath()%>/dept/delete?deptno=" + dno; }
}
</script> <body>
<h1 align="center">部门列表</h1>
<%--显示登录的用户名信息--%>
<%--<% ServletContext servletContext = session.getServletContext();
Object onlinecount = servletContext.getAttribute("onlinecount"); %>--%>
<%--显示用户在线的个数,其信息存储在了 ServletContext()应用域当中了--%>
<h3>在线人数: <%=request.getServletContext().getAttribute("onlinecount")%></h3>
<%--注意这里使用的是 jsp 内置的 session 对象所以不可以,不要把 session 禁用了。--%> <a href="<%=request.getContextPath()%>/user/exit">安全退出系统(手动清除 session 会话信息)</a> <table border="1px" align="center" width="50%">
<tr>
<th>序号</th>
<th>部门编号</th>
<th>部门名称</th>
</tr> <%
// 获取到 request 域当中的数据,并取出来,我们知道是什么类型的数据,直接强制转换
List<Dept> depList = (List<Dept>) request.getAttribute("depList"); int i = 0;
// 循环遍历List,取出数据
for (Dept dept : depList) {
// 在后台输出测试
//System.out.println(dept.getDname());
// 在浏览器显示 测试
//out.write(dept.getDname() + "<br>"); %>
<%-- 浏览器显示测试--%>
<%-- <%=dept.getDname()%>--%> <tr>
<td><%=++i%>
</td>
<td><%=dept.getDeptno()%>
</td>
<td><%=dept.getLoc()%>
</td>
<td>
<!-- javascript:void(0)保持<a>超链接的格式,同时不会发生跳转
我只是希望用户点击该超链接的时候执行一段js代码,不进行页面的跳转 -->
<a href="javascript:void(0)" onclick="del(<%=dept.getDeptno()%>)">删除</a>
<%-- <a href="<%=request.getContextPath()%>/dept/detail?f=m&dno=<%=dept.getDeptno()%>">修改</a>--%>
<%-- <a href="<%=request.getContextPath()%>/dept/detail?f=d&dno=<%=dept.getDeptno()%>">详情</a>--%>
<%-- 这里的修改,和详情都是需要 部门编号,我们可以进行一个统一处理,通过所传的不同的 f= 值作为
标记,用于服务器端的 Servlet 判断转发处理到的--%>
<%-- 或者是--%>
<a href="<%=request.getContextPath()%>/dept/detail?f=edit&dno=<%=dept.getDeptno()%>">修改</a>
<a href="<%=request.getContextPath()%>/dept/detail?f=detail&dno=<%=dept.getDeptno()%>">详情</a>
</td>
</tr> <%
}
%>
</table>
<hr>
<a href="<%=request.getContextPath()%>/add.jsp">新增部门</a>
</body> </html>

6. 总结:

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

  2. 概念:Listener表示监听器,是JavaWeb三大组件(Servlet、Filter、Listener)之一。监听器可以监听就是在application,session,request三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件。Listener分类:JavaWeb中提供了8个监听器。

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

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

  5. 注意:所有监听器中的方法都是不需要javaweb程序员调用的,由服务器来负责调用?什么时候被调用呢?

    当某个特殊的事件发生(特殊的事件发生其实就是某个时机到了。)之后,被web服务器自动调用。

7. 最后:

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,江湖再见,后会有期!!!

Listener监听器,实现一个显示在线用户人数的更多相关文章

  1. ASP.NET MVC使用SignalR统计在线用户人数

    学到新东西就记录一下.也许正好有人需要~~~~~~ 由于需要记录当前在线用户,emmmm又是没做过的... 本来想用数据库的形式,但是想想这么简单的功能百度肯定有.遨游一波百度,有所收获.... 虽然 ...

  2. 使用 Redis 统计在线用户人数

    在构建应用的时候, 我们经常需要对用户的一举一动进行记录, 而其中一个比较重要的操作, 就是对在线的用户进行记录. 本文将介绍四种使用 Redis 对在线用户进行记录的方案, 这些方案虽然都可以对在线 ...

  3. java Session统计在线用户,并且显示在线用户

    关键字: httpsession 1.http://www.jspcn.net/htmlnews/11049329478121583.html       监听器 2.session.invalida ...

  4. 用python爬取B站在线用户人数

    最近在自学Python爬虫,所以想练一下手,用python来爬取B站在线人数,应该可以拿来小小分析一下 设计思路 首先查看网页源代码,找到相应的html,然后利用各种工具(BeautifulSoup或 ...

  5. [Java] JSP笔记 - Listener 监听器

    Java Web 开发时,可以使用 Listener 来监听来监听一些事件,从而实现一些功能.实际上这个监听器,原理就是 Delphi 中大家常用的各种事件. 1. 那么,监听器的主要用途用哪些呢: ...

  6. Servlet监听器及在线用户

    Servlet中的监听器分为三种类型Ⅰ 监听ServletContext.Request.Session作用域的创建和销毁 (1)ServletContextListener (2)HttpSessi ...

  7. listener监听器

    前言:之前写了一篇关于Filter的文章:http://tianweili.github.io/blog/2015/01/26/java-filter/,现在再来一篇Listener的,Filter和 ...

  8. spring listener监听器

    1.Listener的定义与作用 监听器Listener就是在application,session,request三个对象创建.销毁或者往其中添加修改删除属性时自动执行代码的功能组件. Listen ...

  9. Listener 监听器

    Listener的定义与作用 监听器Listener就是在application,session,request三个对象创建.销毁或者往其中添加修改删除属性时自动执行代码的功能组件. Listener ...

  10. Java中的Listener 监听器

    Listener的定义与作用 监听器Listener就是在application,session,request三个对象创建.销毁或者往其中添加修改删除属性时自动执行代码的功能组件. Listener ...

随机推荐

  1. OpenCV常量值含义表

    色彩空间转换常量 常量值 说明 cv2.COLOR_BGR2GRAY 从 BGR 色彩空间转换到 GRAY 色彩空间 cv2.COLOR_RGB2GRAY 从 RGB 色彩空间转换到 GRAY 色彩空 ...

  2. archlinux启动virtualbox实例出现错误:内核驱动未安装

    参照 https://cn.linux-console.net/?p=22258 错误如下 Kernel driver not installed (rc=-1908) The VirtualBox ...

  3. #约数#洛谷 4296 [AHOI2007]密码箱

    题目 给定\(n(n\leq 2*10^9)\),求 \[\sum_{x=1}^n[x^2\bmod n==1] \] 分析 首先当\(n=1\)的时候需要特判, 否则1和\(n-1\)一定是答案, ...

  4. #排列组合,容斥#洛谷 5684 [CSPJX2019]非回文串

    题目 分析 那显然就是\(n!\)减去回文串的方案数 首先如果有超过一个出现奇数次字母那肯定不存在回文串 如果有且仅有一个首先要在次数中选择一个然后其它当偶数处理 偶数那就是首先字母位置选好但顺序可以 ...

  5. webpack 4 快速搭建

    安装 npm install --save-dev webpack@4.30.0 webpack-cli@3.3.2 更新 package.json 脚本 "scripts": { ...

  6. Python调用动态库,获取BSTR字符串

    今天客户在用Python调用我们的动态库的时候,遇到一个问题,调用动态库中的函数,函数返回的是BSTR字符串,但是客户接收到的是一个8位长度的数字. 动态库函数原型:EXTERN_C BSTR ELO ...

  7. ArkUI,更高效的框架设计

     原文:https://mp.weixin.qq.com/s/uSIzuBby7Z92drNDmejKXw,点击链接查看更多技术内容.     上期文章我们讲到了ArkUI的三大特性,同时提到了Ark ...

  8. ArkUI开发趣味体验,快来抽取限量HarmonyOS专属头像!

    本次ArkUI开发趣味体验活动,将手把手教大家如何在IDE里实操一个ArkUI程序,通过补充缺失代码,成功运行程序开启抽奖功能,抽取个人专属头像,做HarmonyOS第一批数字藏品家! 同时本期提供的 ...

  9. AI数字人克隆人直播源码独立部署的应用!

    AI虚拟数字人正在从概念性试验品逐步落地到实际应用场景,特别是在电商直播领域,AI数字人虚拟主播应用可以说是大放异彩,目前,以真人形象为基础的数字人主播,不受场地.真人.布景.灯光.直播设备的限制,相 ...

  10. 在ashx中如何使用session

    前言 都是写陈年往事罢了,如何在ashx 使用session 正文 我们知道在ashx 中使用context.Session 我们即读取不到值,同时设置完也感觉无效. 原因是我们在ashx 中使用的s ...