谈谈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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分享82个节日PPT,总有一款适合您

分享82个节日PPT,总有一款适合您 82个节日PPT下载链接:https://pan.baidu.com/s/1boDTl3PiHFXLJ890CoUfJA?pwd8888 提取码:8888 Python采集代码下载链接:采集代码.zip - 蓝奏云 学习知识费力气,收集整理更不易。…

windows配置go调用python的编译环境

go是支持调用python代码的,之前写了几篇linux的部署教程,因为觉得windows的不复杂就没有写,结果今天新部署一个Windows的环境,有些步骤想不起来了,好记性不如烂笔头,还是记录一下吧。 这些是之前写的linux…

CTF-虚拟机-QEMU-前置知识-操作流程与源码阅读

文章目录 总览内存PCI设备PCI配置空间前64个字节对应源码Memorry空间的BARIO空间的BAR MMIOPMIOIspci访问PCI设备配置空间中的Memory空间和IO空间MMIOPMIO QQM(qemu object model)简洁概要将 TypeInfo 注册 TypeImpl:ObjectClass的初始化&…

SAS聚类分析介绍

1 聚类分析介绍 1.1基本概念 聚类就是一种寻找数据之间一种内在结构的技术。聚类把全体数据实例组织成一些相似组,而这些相似组被称作聚类。处于相同聚类中的数据实例彼此相同,处于不同聚类中的实例彼此不同。聚类技术通常又被称为无监督学习&#xff0…

深入了解Vue.js:构建现代、响应式的前端应用

文章目录 1. Vue.js简介1.1 安装Vue.js 2. Vue的核心概念2.1 数据驱动2.2 组件化2.3 生命周期钩子 3. Vue的特性3.1 响应式数据3.2 模板语法3.3 组件通信 4. 示例项目结语 🎈个人主页:程序员 小侯 🎐CSDN新晋作者 🎉欢迎 &#x1…

Jmeter性能测试 —— 压力模式

压力模式 性能测试中的压力模式有两种。 第一种是并发用户模式(虚拟用户模式)并发用户是指虚拟并发用户数,从业务角度,也可以理解为同时在线的用户数。 从客户端的角度出发,摸底业务系统各节点能同时承载的在线用户数…

pytorch中Conv1d、Conv2d与Conv3d详解

1 卷积介绍 1.1 什么是卷积 卷积(convolution),是一种运算,你可以类比于加,减,乘,除,矩阵的点乘与叉乘等等,它有自己的运算规则,卷积的符号是星号*。表达式…

Linux Makefile的认识及CMake的使用

1 Makefile的作用 Makefile 指的是一个叫 Makefile 的文件,里面提前写了一些指令。每次要自动化的完成一个比较复杂项目的自动编译用的时候,就在命令行输入“make”命令Makefile使用。使用Makefile可以 “智能” 的知道: 1 哪些文件需要先进行编译。 2 当某一文件在某次mak…

Blast中文手册(4)

Extracting data from BLAST databases with blastdbcmd(用blastdbcmd从BLAST数据库中提取数据) Created: June 23, 2008; Updated: January 7, 2021. Extract lowercase masked FASTA from a BLAST database with masking information(从具有掩码信息的BLAST数据库中提取小写掩…

知识管理平台Confluence:win10安装confluence

文章目录 介绍主要功能 安装教程安装java运行平台JRE安装数据库Postgresql在Postgresql创建confluence使用的数据库创建数据库用户创建数据库 安装confluence注册confluence启动confluence 参考链接 介绍 Confluence 是由澳大利亚软件公司 Atlassian 开发的企业协作平台。它提…

2023.11.27 关于 Mybatis 增删改操作

目录 引言 增加用户操作 删除用户操作 修改用户操作 阅读下述文章之间 建议点击下方链接先了解 MyBatis 的创建与使用 MyBatis 的创建与使用 建议点击下方链接先了解 单元测试 的创建与使用 Spring Boot 单元测试的创建与使用 引言 为了方便下文实现增、删、改操作我们先…

推荐3个完美替代 Navicat 的工具

现在企业,mysql数据库用的比较多,mysql数据库客户端的需求也就比较大,navicat就被大家所熟知。 这个工具,确实好用,功能也非常强大,但是,它的强大,是需要付费,或者用一些…

STM32内部温度传感器使用方法详解

STM32内部温度传感器使用方法详解 前言 STM32内部集成了一个片上温度传感器,可以用来测量MCU及周围的温度。测量范围:-40~125,精度1.5℃。虽然精度不高,但在某些应用场景下是够了的,相比于外部接入传感器&#xff0c…

最新Graphviz python安装教程及使用

文章目录 Graphviz 安装python安装graphviz库 Graphviz 安装 Graphviz是一个独立的软件,在用python的pip下载之前,需要先下载软件。 网址:https://graphviz.org/download/ 找到合适的版本进行下载安装。记住自己的安装位置,完…

Linux - 进程间通信

进程通信 初步理解进程通信 所谓进程之间的通信,就是两个进程之间的 数据层面的交互。 我们之前说过,父子进程之间是有一些数据通信的,子进程可以看到一些父进程 允许 子进程访问的数据,比如 父进程的 环境变量,子…

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

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

InsCode实践分享

在当今信息爆炸的时代,如何从海量信息中脱颖而出,获取更多的关注和认可,成为了许多人的共同追求。作为知乎平台上的优质用户,我愿意分享一些自己的经验和技巧,帮助大家更好地运用InsCode,实现个人成长和进步…

【爬虫逆向分析实战】某笔登录算法分析——本地替换分析法

前言 作者最近在做一个收集粉币的项目,可以用来干嘛这里就不展开了😁,需要进行登录换算token从而达到监控收集的作用,手机抓包发现他是通过APP进行计算之后再请求接口的,通过官网分析可能要比APP逆向方便多&#xff0…

01-使用Git操作本地库,如初始化本地库,提交工作区文件到暂存区和本地库,查看版本信息,版本切换命令等

Git的使用 概述 Git是一个分布式版本控制工具, 通常用来管理项目中的源代码文件(Java类、xml文件、html页面等)进行管理,在软件开发过程中被广泛使用 Git可以记录文件修改的历史记录并形成备份从而实现代码回溯, 版本切换, 多人协作, 远程备份的功能Git具有廉价的本地库,方便…

开源图床Qchan本地部署远程访问,轻松打造个人专属轻量级图床

文章目录 前言1. Qchan网站搭建1.1 Qchan下载和安装1.2 Qchan网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar云端设置2.2 Cpolar本地设置 3. 公网访问测试总结 前言 图床作为云存储的一项重要应用场景,在大量开发人员的努力下,已经开发出大…