板块一 Servlet编程:第十节 监听器全解 来自【汤米尼克的JAVAEE全套教程专栏】

news/2025/2/22 0:44:14/

板块一 Servlet编程:第十节 监听器全解

  • 一、什么是监听器
    • 实现一个监听器的简单流程
  • 二、各对象的监听器使用方法
    • (1)Request域的监听器
    • (2)Session域的监听器
    • (3)Application域的监听器
  • 三、实例:监听器实现在线人数统计

在上一节中,我们学习的过滤器在JavaEE中主要对请求响应的过程做过滤,那么这节学习的监听器则主要在作用域生命周期的过程中做监听,二者都为我们实现了一两拨千斤的效果

一、什么是监听器

  • 类似于前端中事件绑定,Java中的监听器用于监听web应用中某些对象(如ServletContext\HttpSeesion\ServletRequest等)、信息的创建、销毁、增加,修改,删除等动作的发生,然后作出相应的响应处理。当范围对象的状态发生变化的时候,服务器自动调用监听器对象中的方法。常用于统计在线人数和在线用户,系统加载时进行信息初始化,统计网站的访问量等等
  • 监听器按照不同监听对象可以分成三类,一共提供了八个监听器接口

第一类:Request对象的监听器

java">ServletRequestListener           (处理request对象创建和销毁)
ServleRequestAttributeListener   (处理域对象中的数据添加 替换 删除)

第二类:Session对象的监听器

java">HttpSessionListener               (处理session对象创建和销毁)
HttpSessionAttributeListener      (处理session域对象中的数据添加 修改 删除)
HttpSessionBindingListener        (处理session对象监听器绑定和解绑定接口)
HttpSessionActivationListener     (处理session对象钝化和活化状态接口)

第三类:Application对象的监听器

java">ServletContextListener            (处理application对象创建和销毁)
ServletContextAttributeListener   (处理application域对象中的数据添加 修改 删除)

实现一个监听器的简单流程

实例:监听一个Seesion的生命周期
同样在www.caijiyuan下新建监听层的包Listener
在这里插入图片描述

包中新建监听器接口listenerSession,继承自HttpSessionListener 类,然后重写HttpSessionListener 监听Session生命周期的两个方法

java">@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
}

在这两个方法中写入相应周期时要打印的内容方便观察
然后同样,别忘了在类前写入监听器的注释@WebListener,我们点开监听器的注释代码,会发现没有形参
在这里插入图片描述
因此监听器的注释只用写成@WebListener即可,不必配置文件站点名。另外,监听器与过滤器一样可以在web.xml中配置,但不如注释简洁
此时,监听器完整代码为

java">package www.caijiyuan.Listener;import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;@WebListener
public class listenerSession implements HttpSessionListener {@Overridepublic void sessionCreated(HttpSessionEvent httpSessionEvent) {System.out.println("Session被创建了");}@Overridepublic void sessionDestroyed(HttpSessionEvent httpSessionEvent) {System.out.println("Seesion被销毁了");}
}

在start.java文件中写一个Session的创建销毁过程

java">package www.caijiyuan.Servlt;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 javax.servlet.http.HttpSession;
import java.io.IOException;@WebServlet("/start")
public class start extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("访问到Start");HttpSession session =req.getSession(); //创建Sessionsession.invalidate(); //销毁Session}
}

此时的项目结构为
在这里插入图片描述

启动服务器,在浏览器中访问start.java
在这里插入图片描述
即会在控制台打印
在这里插入图片描述
这就是监听器最简单的实现流程

二、各对象的监听器使用方法

(1)Request域的监听器

类似的,Request对象的监听器可以这样定义

java">import javax.servlet.*;@WebListener
public class MyRequestListener implements ServletRequestListener, ServletRequestAttributeListener {@Overridepublic void requestDestroyed(ServletRequestEvent sre) {// 监听HttpServletRequest对象的销毁  项目中任何一个Request对象的销毁都会触发该方法的执行ServletRequest servletRequest = sre.getServletRequest();System.out.println("request"+servletRequest.hashCode()+"对象销毁了");}@Overridepublic void requestInitialized(ServletRequestEvent sre) {// 监听HttpServletRequest对象的创建并初始化 项目中任何一个Request对象的创建并初始化都会触发该方法的执行ServletRequest servletRequest = sre.getServletRequest();System.out.println("request"+servletRequest.hashCode()+"对象初始化");}@Overridepublic void attributeAdded(ServletRequestAttributeEvent srae) {// 任何一个Request对象中调用 setAttribute方法增加了数据都会触发该方法ServletRequest servletRequest = srae.getServletRequest();String name = srae.getName();Object value = srae.getValue();System.out.println("request"+servletRequest.hashCode()+"对象增加了数据:"+name+"="+value);}@Overridepublic void attributeRemoved(ServletRequestAttributeEvent srae) {// 任何一个Request对象中调用 removeAttribute方法移除了数据都会触发该方法ServletRequest servletRequest = srae.getServletRequest();String name = srae.getName();Object value = srae.getValue();System.out.println("request"+servletRequest.hashCode()+"对象删除了数据:"+name+"="+value);}@Overridepublic void attributeReplaced(ServletRequestAttributeEvent srae) {// 任何一个Request对象中调用 setAttribute方法修改了数据都会触发该方法ServletRequest servletRequest = srae.getServletRequest();String name = srae.getName();Object value = srae.getValue();Object newValue=servletRequest.getAttribute(name);System.out.println("request"+servletRequest.hashCode()+"对象增修改了数据:"+name+"="+value+"设置为:"+newValue);}
}

对应的测试start.java

java">@WebServlet("/start")
public class start extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setAttribute("name", "Toomynike");req.setAttribute("name", "NikeTommy");req.removeAttribute("name");}
}

(2)Session域的监听器

对于Session域的四个监听器接口:HttpSessionListener、HttpSessionAttributeListener、HttpSessionBindingListener、HttpSessionActivationListener中的前两个,可以这样使用

java">import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;@WebListener
public class MySessionListener implements HttpSessionListener , HttpSessionAttributeListener {@Overridepublic void sessionCreated(HttpSessionEvent se) {System.out.println("任何一个Session对象创建");}@Overridepublic void sessionDestroyed(HttpSessionEvent se) {System.out.println("任何一个Session对象的销毁");}@Overridepublic void attributeAdded(HttpSessionBindingEvent se) {System.out.println("任何一个Session对象中添加了数据");}@Overridepublic void attributeRemoved(HttpSessionBindingEvent se) {System.out.println("任何一个Session对象中移除了数据");}@Overridepublic void attributeReplaced(HttpSessionBindingEvent se) {System.out.println("任何一个Session对象中修改了数据");}
}

对于Sessiony域监听器的HttpSessionBindingListener接口可以这样使用

java">import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;@WebListener
public class MySessionActivationListener implements HttpSessionActivationListener {@Overridepublic void sessionWillPassivate(HttpSessionEvent se) {System.out.println("session即将钝化");}@Overridepublic void sessionDidActivate(HttpSessionEvent se) {System.out.println("session活化完毕");}
}

(3)Application域的监听器

对于Application域的ServletContextListener , ServletContextAttributeListener监听器接口,可以这样使用

java">import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;public class MyApplicationListener implements ServletContextListener , ServletContextAttributeListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {System.out.println("ServletContext创建并初始化了");}@Overridepublic void contextDestroyed(ServletContextEvent sce) {System.out.println("ServletContext销毁了");}@Overridepublic void attributeAdded(ServletContextAttributeEvent scae) {System.out.println("ServletContext增加了数据");}@Overridepublic void attributeRemoved(ServletContextAttributeEvent scae) {System.out.println("ServletContext删除了数据");}@Overridepublic void attributeReplaced(ServletContextAttributeEvent scae) {System.out.println("ServletContext修改了数据");}
}

三、实例:监听器实现在线人数统计

这个功能实现的抽象逻辑为:

  • 当有新的Session对象创建时,在线人数+1
  • 当有Session对象被销毁时,在线人数-1

在Listener层下新建OnlineListener监听器用于实现核心逻辑,继承自HttpSessionListener之后,重写两个监听方法实现抽象逻辑

java">package www.caijiyuan.Listener;import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;@WebListener
public class OnlineListener implements HttpSessionListener {private Integer onlineNumber = 0; //保存在线人数@Overridepublic void sessionCreated(HttpSessionEvent httpSessionEvent) {onlineNumber++;httpSessionEvent.getSession().setAttribute("OnlineListener",onlineNumber); //将在线人数值存入Session}@Overridepublic void sessionDestroyed(HttpSessionEvent httpSessionEvent) {onlineNumber--;httpSessionEvent.getSession().setAttribute("OnlineListener",onlineNumber);}
}

其中

java">httpSessionEvent.getSession().setAttribute("OnlineListener",onlineNumber); //将在线人数值存入Session

就是调用了httpSessionEvent实例,它可以把监听器中得到的参数存入Session方便我们在别的Servlet中调取

在start.java中访问创建Session并在浏览器打印在线人数

java">package www.caijiyuan.Servlt;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 javax.servlet.http.HttpSession;
import java.io.IOException;@WebServlet("/start")
public class start extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("访问到Start");HttpSession session =req.getSession(); // 用户来了创建Session// 得到当前作用域中的在线人数Integer onlineNumber = (Integer) session.getAttribute("OnlineListener");// 设置相应类型及编码resp.setContentType("text/html;charset=UTF-8");// 打印在线人数resp.getWriter().write("<h1>当前在线人数:"+onlineNumber+"</h1>");}
}

启动服务器先后在本机电脑上的Google浏览器、Edge浏览器中访问start文件,即可得到
在这里插入图片描述
在这里插入图片描述
看似成功了,但还有一个小bug:当我们在线人数为2时,刷新Google浏览器会发现在线人数只会显示1,这是因为存储在Session中只能在当前浏览器中访问,数据不共享。此时就需要扩大域对象的范围了,把数据存在服务器数据域上:ServletContext对象。
还记得我们在ServletContext小节学习的获取 ServletContext 对象的几种方法吗?
在这里插入图片描述
其中的通过Session获取ServletContext 对象看似绕了一步,没必要,实则就在这里起到了作用,因为监听器给予的对象里面只有Session,因此只能通过Session获取ServletContext 对象
现在思路就很明确了,我们只需要将OnlineListener类中的两句

java">httpSessionEvent.getSession().setAttribute("OnlineListener",onlineNumber);

改成

java">httpSessionEvent.getSession().getServletContext().setAttribute("OnlineListener",onlineNumber);

在start.java中,将接受参数作用域也改成ServletContext对象

java">package www.caijiyuan.Servlt;import javax.servlet.ServletContext;
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 javax.servlet.http.HttpSession;
import java.io.IOException;@WebServlet("/start")
public class start extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("访问到Start");HttpSession session =req.getSession(); // 用户来了创建Session// 得到当前作用域中的在线人数ServletContext servletContext = getServletContext();Integer onlineNumber = (Integer) servletContext.getAttribute("OnlineListener");// 设置相应类型及编码resp.setContentType("text/html;charset=UTF-8");// 打印在线人数resp.getWriter().write("<h1>当前在线人数:"+onlineNumber+"</h1>");}
}

与刚刚相同,先后在两个浏览器访问start文件,再刷新两个浏览器就能同步在线人数了
在这里插入图片描述

以上就是本小节,也是本专栏Servlet板块的全部内容了,欢迎读者在评论区提出意见反馈。博主也很乐于私信交流。下一个板块我们将着重详解Java EE的另一个基石:JSP和JSTL


http://www.ppmy.cn/news/1574014.html

相关文章

MySQL多表连接查询高阶技巧和高阶函数

MySQL多表连接查询高阶技巧和高阶函数 以下是 MySQL 中多表连接查询的高阶技巧和高阶函数的详细介绍&#xff1a; 一、多表连接查询高阶技巧 1. 减少连接次数 技巧&#xff1a;通过子查询或临时表预先处理部分数据&#xff0c;减少多表连接的复杂度和次数&#xff0c;从而提…

QSNCTF做题记录-应急响应

题目来源&#xff1a;青少年CTF <天狩CTF竞赛平台> 应急响应一 题目描述&#xff1a;题目请使用SSH连接。用户名root&#xff0c;密码qsnctf&#xff0c;请提交当前系统发行版信息&#xff0c;得到的结果请包含qsnctf{}提交 1&#xff0c;首先使用kali-linux启用SSH服务连…

《跟李沐学 AI》AlexNet论文逐段精读学习心得 | PyTorch 深度学习实战

前一篇文章&#xff0c;使用 AlexNet 实现图片分类 | PyTorch 深度学习实战 本系列文章 GitHub Repo: https://github.com/hailiang-wang/pytorch-get-started 本篇文章内容来自于学习 9年后重读深度学习奠基作之一&#xff1a;AlexNet【下】【论文精读】】的心得。 《跟李沐…

AI客服-接入deepseek大模型到微信(本地部署deepseek集成微信自动收发消息)

1.本地部署 1.1 ollama Ollama软件通过其高度优化的推理引擎和先进的内存管理机制&#xff0c;显著提升了大型语言模型在本地设备上的运行效率。其核心采用了量化技术&#xff08;Quantization&#xff09;以降低模型的计算复杂度和存储需求&#xff0c;同时结合张量并行计算&…

企业软件合规性管理:构建高效、安全的软件资产生态

引言 在数字化转型的浪潮下&#xff0c;企业的软件使用方式日益多元化&#xff0c;涉及云端、订阅制、永久授权及浮动许可等多种模式。然而&#xff0c;随着软件资产的增多&#xff0c;企业面临着合规性管理的严峻挑战&#xff1a;非法软件使用、许可证管理不当、软件资产闲置…

Sphinx和Read the Docs快速生成多语言版本的文档

下面是调整后的默认语言为简体中文&#xff0c;并添加英文翻译的具体操作步骤&#xff1a; 一、Sphinx本地配置调整 修改conf.py 将默认语言设置为中文&#xff0c;并声明支持英文&#xff1a; language zh_CN # 默认语言改为中文 locale_dirs [locale/] # 翻译文件目录不变…

qt + opengl 给立方体增加阴影

在前几篇文章里面学会了通过opengl实现一个立方体&#xff0c;那么这篇我们来学习光照。 风氏光照模型的主要结构由3个分量组成&#xff1a;环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照。下面这张图展示了这些光照分量看起来的样子&#xff1a; 1 环境光照(Ambient …

PHP日期和时间Date()函数获取当前时间

在PHP编程中&#xff0c;获取当前日期和时间是一项基本而又频繁的操作&#xff0c;这一任务可以通过内建的 date()函数轻松实现。此函数不仅灵活多变&#xff0c;还支持多种日期和时间格式化选项&#xff0c;使得开发者能够根据具体需求定制输出格式。以下是对 date()函数及其在…