谈谈Listener

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬
Tomcat三大组件:Servlet、Listener、Filter,今天来简单复习一下Listener。此前如果从来没学习过三大组件的同学,可以去B站搜索:JavaWeb 崔希凡,选择感兴趣的知识点自行复习。

复习Listener

大致来说,常用的监听器就是“6+2”:

6个常规监听器
    |---ServletContext
            |---ServletContextListener(生命周期监听)
            |---ServletContextAttributeListener(属性监听)
    |---HttpSession
            |---HttpSessionListener(生命周期监听)
            |---HttpSessionAttributeListener(属性监听)
    |---ServletRequest
            |---ServletRequestListener(生命周期监听)
            |---ServletRequestAttributeListener(属性监听)
2个感知监听
    |---HttpSessionBindingListener
    |---HttpSessionActivationListener

感知监听都是Session相关的,我已经在《Cookie与Session》一文中讲过,这里就不提了。

6个常规监听器,分属三类,分别对应JavaWeb三大域对象(除去JSP的Page域):ServletContext、HttpSession、ServletRequest。共三对,每一对都包括1个生命周期监听和1个属性监听。

所谓生命周期监听器,就是监听三大域对象的创建和销毁。每当Tomcat创建或销毁三大域对象,都会被这些监听器察觉,然后它们会做相应的操作(调用自身的特定方法)。

属性监听器则专门监听三大域对象get/setAttribute()。每当我们给域对象设置值或者从里面取值,都会被它们监听到,然后还是触发它们特定的方法。

先复习一下Servlet如何处理请求:

然后我们来看看监听器到底在什么时候做了什么事。

为了帮助大家理解接下来这张图的细节,我问几个问题,大家带着问题去看:

  • 三大生命周期监听器,各自在何时创建、销毁,顺序是怎么样的?
  • 访问一个Servlet,HttpSessionListener一定会触发吗?(换个角度就是,访问Servlet,Session一定会创建吗?)
  • 三大属性监听何时触发?

属性监听没画出来,自行脑补

答案:

问题一

  1. 在项目启动时ServletContextListener监听到ServletContext对象创建
  2. 每一次请求Tomcat都会创建一个Request,它的创建会被ServletRequestListener监听到
  3. 如果Servlet中调用了request.getSession(),则Tomcat会创建Session(如果根据JSESSIONID找不到对应的),这会被HttpSessionListener监听到
  4. 请求结束,Request销毁,被监听到
  5. 用户30分钟未访问,Session过期销毁,被监听到
  6. 项目关停,ServletContext销毁,被监听到

问题二

只有当在Servlet中调用request.getSession(),且根据JSESSIONID找不到对于的Session时,才会创建新的Session对象,才会被监听到。第二次请求,浏览器会带上JSESSIONID,此时虽然还是request.getSession(),但是会返回上次那个。根据JSESSIONID去Session这个过程是隐式的,我们看到的就是getSession()。

问题三

对于图中的步骤7/11/12,也就是get/setAttribute()时,会触发属性监听。

搞清楚每一种监听器的作用以及触发时机是最重要的,使用其实很简单。去看崔老师视频吧,讲得很清楚了。

观察者模式

大家觉得Listener难在哪?

是监听器种类太多记不清吗?不会吧,不就是“6+2”吗。

是各个监听器的触发时机扑朔迷离吗?也不会啊,上面的图讲得很清楚了。

其实!大家觉得监听器难,是因为根本不知道为啥它能起作用。监听器的底层原理其实涉及到一种设计模式:观察者模式(Observer)。

先来看监听器的定义:

监听器就是一个实现了特定接口的普通Java程序,这个程序专门用于监听另一个Java对象的方法调用或者属性改变。当被监听对象发生上述事件后,监听器某个方法将立即被执行。

三个概念:被监听对象、事件、监听器对象

道理都懂,但是还是没讲到点子上。为啥监听器能监听?

程序毕竟不是人,不像警察盯着小偷,看到他偷东西就动手抓人。在我看来,一个方法能执行,必然是被调用!有两种可能:

  • 定时任务
  • 对象调用/静态方法调用

监听器肯定不是通过定时任务实现的,毕竟它的方法调用时机是在“被监听对象特定行为发生时”。既然不是定时任务,那么肯定是被监听对象主动告诉监听器的!

是不是觉得很荒唐?被监听对象主动告诉监听对象,那还叫监听器?监听个鬼哦...

但是大家有没有想过,监听器英文名叫Listener,我翻译成“倾听者”有何不可?就是说,Listener对象一直在侧耳倾听,等待被监听对象发号施令。这个翻译骚不骚?

也就是说:被监听对象发生某个行为时,会主动告诉Listener(对象方法调用),让它执行对应的特定操作!

代码结构

被监听对象

// 被监听对象
public class Thief {

	private ThiefListener listener;

	public void registerListener(ThiefListener listener) {
		this.listener = listener;
	}

	public void steal() {
		// 偷之前,告诉警察
		if (listener != null) {
			Event event = new Event(this);
			// 喂,有胆开枪啊!
			listener.shot(event);
		}
		// 偷东西
		System.out.println("to steal money...");
	}

}

监听器接口

// 它的实现类就是监听器对象
public interface ThiefListener {
	void shot(Event event);
}

事件(就是包装后的被监听对象)

public class Event {

	private Thief thief;

	public Event() {
	}

	public Event(Thief thief) {
		this.thief = thief;
	}

	public Thief getThief() {
		return thief;
	}

	public void setThief(Thief thief) {
		this.thief = thief;
	}
}

测试

public class ObserverTest {
	public static void main(String[] args) {
		// 被监听对象
		Thief thief = new Thief();

		// 监听器,直接new一个接口的匿名类对象
		ThiefListener thiefListener = new ThiefListener() {
			public void shot(Event event) {
                // 我这里并没有用到event,实际上可以从event取出事件源
				System.out.println("啪啪啪!!!!");
			}
		};

		// 注册监听
		thief.registerListener(thiefListener);

		// 特定行为,触发监听器:内部调用listener.shot()
		thief.steal();
	}
}
作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

进群,大家一起学习,一起进步,一起对抗互联网寒冬

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/209540.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

图书馆座位预约时间冲突提示(前后端全) 前端elementUI 时间选择器只显示时和分,SQL实现时间冲突判断

背景 帮客户定制项目,要实现图书馆预约座位的功能。 功能描述如下:学生选择开始时间和结束时间,只选择小时和分钟,提交预约后,如果该时间有冲突提示学生修改预约时间。 问题 前端样式选择的是elmentUI,但…

JavaScript编程基础 – 闭包(Closure)

JavaScript编程基础 – 闭包 JavaScript Programming Essentials - Closure By JacksonML 闭包和JavaScript的作用域有关。 我们需要先理解闭包的概念。 本文简要介绍闭包函数以及环境状态,并用实例说明闭包的创建及其基本用法。希望对学习及开发有所帮助。 1. …

公共部门生成式人工智能的未来

作者:Dave Erickson 最近,我与 IDC Government Insights 研究副总裁阿德莱德奥布莱恩 (Adelaide O’Brien) 坐下来讨论了全球公共部门生成式人工智能的当前和未来状况。 完整的对话可以按需查看,但我也想强调讨论中的一些要点。 我们的目标是…

【Geoserver】SLD点位样式(PointSymbolizer)设计全通

SLD文件可以控制geoserver的样式管理,这里专门针对点位进行设计,首先点位的设计需要用到这面这个大标签 之前的项目中已经用到了很多关于面的样式管理,这里新学习的是关于点的样式管理 PointSymbolizer 参考资料地址:https://doc…

深入解析SpringBoot的请求响应机制

SpringBootWeb请求响应 前言1. 请求1.1 Postman介绍 1.2 简单参数1.2.1 原始方式1.2.2 SpringBoot方式1.2.3 参数名不一致 1.3 实体参数1.3.1 简单实体对象1.3.2 复杂实体对象 1.4 数组集合参数1.4.1 数组1.4.2 集合 1.5 日期参数1.6 JSON参数1.7 路径参数 2. 响应2.1 Response…

C#图像处理OpenCV开发指南(CVStar,06)——图片光滑处理之高斯滤波(GaussianBlur)的实现代码

1 高斯滤波 原理就不讲了,文章很多。 基础代码请参阅: C#图像处理OpenCV开发指南(CVStar,03)——基于.NET 6的图像处理桌面程序开发实践第一步https://blog.csdn.net/beijinghorn/article/details/134684471?spm10…

RHCSA学习笔记(RHEL8) - Part1.RH124

Chapter Ⅰ 入门 - Linux 开源系统,命令行,模块化(软件包的形势) - Windows 闭源Linux是类UNIX系统,mac系统也是类UNIX系统,所以二者的图形化界面比较相似 开源许可证:公共版权;宽…

uniapp-从后台返回的一串地址信息上,提取省市区进行赋值

1.这是接口返回的地址信息 2.要实现的效果 3.实现代码&#xff1a; <view class"address">{{item.address}}</view>listFun() {let url this.$url.url.positionInfoCompany;let param {page: this.page,limit: this.limit,keyword: this.keyword,};thi…

Rust语言项目实战(二) - 准备键盘和终端屏幕

上一章节中&#xff0c;我们实现了游戏开始音频的播放&#xff0c;本章我们开始编写游戏界面。我们的游戏是在命令行终端中运行的&#xff0c;因此编写的界面也是终端中展示的界面&#xff0c;上一章中&#xff0c;我们已经把相关的依赖包crossterm添加到了依赖列表中。本章首先…

【嵌入式Linux程序开发综合实验】-1(附流程图) | ARM开发板 | 测试“Hello World” | Makefile文件 | 实现加法相加

任务&#xff1a;编写在标准输出终端输出“Hello World&#xff01;”的C语言代码以及输入指定数字相加结果、Makefile&#xff0c;并分别编译出在PC与ARM上运行的可执行程序文件。 设备以及工具 硬件&#xff1a;Linux开发板、PC机、串口连接线 图1 Linux开发板以及串口接线 …

原生video设置控制面板controls显示哪些控件

之前我们学习了如何使用原生video播放视频 今天来一个进阶版的——设置控制面板controls显示哪些控件 先看一下当我们使用原生video时&#xff0c;controls属性为true时&#xff0c;相关代码如下&#xff1a; 正常的控制面板默认显示的控件有&#xff1a;播放、时间线、音量调…

四、Zookeeper节点类型

目录 1、临时节点 2、永久节点 Znode有两种,分别为临时节点和永久节点。 节点的类型在创建时即被确定,并且不能改变。 1、临时节点 临时节点的生命周期依赖于创建它们的会话。一旦会话结束,临时节点将被自动删除,

机器学习笔记 - 基于百度飞桨PaddleSeg的人体分割模型以及TensorRT部署说明

一、简述 虽然Segment Anything用于图像分割的通用大模型看起来很酷(飞桨也提供分割一切的模型),但是个人感觉落地应用的时候心里还是更倾向于飞桨这种场景式的,因为需要用到一些人体分割的需求,所以这里主要是对飞桨高性能图像分割开发套件进行了解和使用,但是暂时不训练…

LiteOS内存管理:TLSF算法

问题背景 TLSF算法主要是面向实时操作系统提出的&#xff0c;对于RTOS而言&#xff0c;执行时间的确定性是最根本的&#xff0c;然而传统的动态内存分配器&#xff08;DMA&#xff0c;Dynamic Memory Allocator&#xff09;存在两个主要问题&#xff1a; 最坏情况执行时间不确…

Vue3+nuxt+ts项目引入高德地图API实现步骤

看了好多相关的文章都没有完全贴合选用Vue3nuxtts框架的&#xff0c;也不太靠谱&#xff0c;只好自己踩坑实现了 首先去高德开放平台用自己的账号申请一个key&#xff0c;位置如下&#xff0c;申请好后保存好生成的key 我们使用vuemap/vue-amap&#xff0c;一个高德地图2.0版本…

微信小程序自定义tabBar简易实现

文章目录 1.app.json设置custom为true开启自定义2.根目录创建自定义的tab文件3.app.js全局封装一个设置tabbar选中的方法4.在onshow中使用选中方法最终效果预览 1.app.json设置custom为true开启自定义 2.根目录创建自定义的tab文件 index.wxml <view class"tab-bar&quo…

Vue + Element ui 实现动态表单,包括新增行/删除行/动态表单验证/提交功能

原创/朱季谦 最近通过Vue Element ui实现了动态表单功能&#xff0c;该功能还包括了动态表单新增行、删除行、动态表单验证、动态表单提交功能&#xff0c;趁热打铁&#xff0c;将开发心得记录下来&#xff0c;方便以后再遇到类似功能时&#xff0c;直接拿来应用。 简化的页…

用通俗的方法讲解:大模型微调训练详细说明(附理论+实践代码)

本文内容如下 介绍了大模型训练的微调方法&#xff0c;包括prompt tuning、prefix tuning、LoRA、p-tuning和AdaLoRA等。 介绍了使用deepspeed和LoRA进行大模型训练的相关代码。 给出了petals的介绍&#xff0c;它可以将模型划分为多个块&#xff0c;每个用户的机器负责其中一…

七、FreeRTOS之FreeRTOS中断管理

这部分非常重要&#xff0c;小伙伴们必须要掌握的哈~本节需要学的内容如下&#xff1a; 1&#xff0c;什么是中断&#xff1f;&#xff08;了解&#xff09; 2&#xff0c;中断优先级分组设置&#xff08;熟悉&#xff09; 3&#xff0c;中断相关寄存器&#xff08;熟悉&…

VLAN间路由详细讲解

本次实验拓扑的主要概述以及设计到的相关技术 VLAN技术&#xff1a; VLAN&#xff08;Virtual Local Area Network&#xff09;即虚拟局域网&#xff0c;是将一个物理的LAN在逻辑上划分成多个广播域的通信技术。 每个VLAN是一个广播域&#xff0c;VLAN内的主机间可以直…