说说Spring事件发布机制

news2025/1/23 13:17:47

文章目录

  • 前言
  • 一、 使用到事件发布机制的源码
  • 二、Springboot启动过程中用到的部分事件
  • 三、Springboot中的监听器
  • 四、自定义事件源,事件监听器和事件发布器
    • 4.1 目录结构
    • 4.2 事件源
    • 4.3 事件监听器
    • 4.4 事件帮助器
      • 4.4.1 事件发布帮助接口
      • 4.4.2 事件发布帮助接口实现类
      • 4.4.3 抽象的事件监听器
      • 4.4.4 事件发布入口的Controller和Service
      • 4.4.5 获取applicationContext对象工具类
      • 4.4.5 启动Springboot后发送请求
  • 五、事件发布机制原理
    • 5.1 自己对事件发布机制的理解
    • 5.2 事件发布机制源码分析

前言

在最近分析和写的SpringBoot源码分析(面试官:你说说Springboot的启动过程吧(5万字分析启动过程))中,给自己留了几个扩展内容,其中一个是Spring的事件机制,在分析源码的过程中,也是大量使用了事件机制,在我分析的这篇博客中,有不少地方都运用了事件发布机制,所以本文的目的是从SpringBoot中学习到事件的发布流程,并自己写一个事件发布用于以后得业务。

一、 使用到事件发布机制的源码

前言中提到的源码分析文章中SpringBoot源码分析中使用到的事件发布机制的小节有:

  • A.5、发布启动的事件并由应用程序启动监听器进行处理
  • A.6.4 监听环境准备
  • B.2.3 发布上下文准备的事件
  • B.2.4 发布引导程序关闭的事件
  • B.2.8 发布上下文加载事件并处理事件
  • “B.3.12、最后一步:发布相应的事件” 中的 “3)为生命周期处理器进行广播更新”
  • “B.3.12、最后一步:发布相应的事件” 中的 “4)发布最终的事件”
  • B.6、发布上下文开始的事件
  • B.8.2、发布失败的事件给监听器
  • B.9、发布准备就绪的事件

二、Springboot启动过程中用到的部分事件

事件Event说明
ApplicationStartingEvent一旦SpringApplication启动,事件就会尽早发布 - 在Environment或ApplicationContext可用之前,但在 ApplicationListener注册之后。事件的来源是SpringApplication本身,但要注意在这个早期阶段过多地使用其内部状态,因为它可能会在生命周期的后期被修改
ApplicationEnvironmentPreparedEvent当SpringApplication启动时,且Environment首先可用于检查和修改
ApplicationContextInitializedEvent当SpringApplication启动并准备ApplicationContext并且已调用 ApplicationContextInitializers 时,但在加载任何 Bean 定义之前,发布此事件
BootstrapContextClosedEvent引导程序在关闭时发布此事件
ApplicationPreparedEvent事件发布为SpringApplication启动时,并且ApplicationContext已完全准备好,但未刷新。Bean的定义将被加载,并且Enviroment已准备好在此阶段使用
ServletWebServerInitializedEvent在WebServer准备好之后发布此事件,对于获取运行服务的本地端口是有用的
ContextStartedEvent启动应用程序上下文时引发的事件。
ContextRefreshedEvent当ApplicationContext已经初始完成或已经更新完成后发布此事件
ApplicationStartedEvent一旦应用程序上下文刷新后,而ApplicationRunner和CommandLineRunner被调用之前就发布此事件
ApplicationFailedEvent启动失败时由SpringApplication发布的事件
ApplicationReadyEvent尽可能晚地发布事件,以指示应用程序已准备好为请求提供服务。事件源是SpringApplication本身,但请注意不要修改其内部状态,因为届时所有初始化步骤都将完成
ContextStoppedEvent当容器停止时发布,即调用stop()方法, 即所有的Lifecycle bean都已显式接收到了stop信号 , 关闭的容器可以通过start()方法重启
ContextClosedEvent当容器关闭时发布,即调用close方法, 关闭意味着所有的单例bean都已被销毁.关闭的容器不能被重启或refresh
SpringApplicationEvent与SpringApplication相关的ApplicationEvent的基类

我看了一下以上这些事件源所在的包的分布:
类似Application***Event事件,以及BootstrapContextClosedEventServletWebServerInitializedEvent都是在Spring-boot包下的:
在这里插入图片描述
其中SpringApplicationEvent是这些Application***Event事件的父类,EventPublishingRunListener是集中了各个事件的发布的方法。
而类似Context***Event事件则是在spring-context包下的:
在这里插入图片描述
其中ApplicationContextEvent是类似Context***Event事件的父类。
SpringApplicationEventApplicationContextEvent又都继承了ApplicationEventEventObjectApplicationEvent的父类。

三、Springboot中的监听器

我们拿ApplicationStartingEvent事件来说,这个事件其实并不是只被一个监听器监听:
在这里插入图片描述
同时被RestartApplicationListenerLoggingApplicationListener监听:
RestartApplicationListener类实现了ApplicationListener<ApplicationEvent>重写了onApplicationEvent方法:
在这里插入图片描述
LoggingApplicationListener类实现了GenericApplicationListener,但这个GenericApplicationListener接口继承了SmartApplicationListener接口,而SmartApplicationListener接口又继承了ApplicationListener<ApplicationEvent>
在这里插入图片描述
LoggingApplicationListener类重写了onApplicationEvent方法:
在这里插入图片描述
可以看到Springboot中的监听器进行了多事件源的监听,在根据instanceof关键字进行判断,对不同的事件源进行处理。
我们再看一个事件:ApplicationEnvironmentPreparedEvent:
在这里插入图片描述
这个事件被7个监听器进行监听和分别处理,这些监听器毫无疑问也都间接或直接实现或继承了ApplicationListener<ApplicationEnvironmentPreparedEvent>

这里罗列一下部分事件对应的监听器:

事件Event事件的监听器
ApplicationStartingEventRestartApplicationListener、LoggingApplicationListener
ApplicationEnvironmentPreparedEventRemoteUrlPropertyExtractor、SpringBootContextLoader、FileEncodingApplicationListener、AnsiOutputApplicationListener、BackgroundPreinitializer
ApplicationContextInitializedEvent-
BootstrapContextClosedEvent-
ApplicationPreparedEventRestartApplicationListener、LoggingApplicationListener
ServletWebServerInitializedEvent-
ContextStartedEvent-
ContextRefreshedEventClearCachesApplicationListener、ScheduledAnnotationBeanPostProcessor、SharedMetadataReaderFactoryContextInitializer.SharedMetadataReaderFactoryBean、 ParentContextApplicationContextInitializer.EventPublisher
ApplicationStartedEvent-
ApplicationFailedEventRestartApplicationListener、BackgroundPreinitializer
ApplicationReadyEventRestartApplicationListener、BackgroundPreinitializer、ConditionEvaluationDeltaLoggingListener、SpringApplicationAdminMXBeanRegistrar
ContextStoppedEvent-
ContextClosedEventSpringApplicationShutdownHook.ApplicationContextClosedListener 、ParentContextCloserApplicationListener.ContextCloserListener、LoggingApplicationListener
SpringApplicationEventBackgroundPreinitializer、ApplicationPidFileWriter

有些事件我没有找到对应的监听器,这个我在分析源码的时候也发现了,目前我还没有找到答案,知道答案的大佬还望不吝赐教哦~

四、自定义事件源,事件监听器和事件发布器

结合Springboot中的事件和监听器的关系,其实我们在自己的实现中也可以一个事件被多个监听器监听,只是我们在业务实现中一搬都是一对一的关系。

4.1 目录结构

在这里插入图片描述

自定义事件发布机制说明:

  • 这个事件发布还是基于SpringBoot源码分析(面试官:你说说Springboot的启动过程吧(5万字分析启动过程))文章中的项目来写的。
  • 关于事件发布的内容我都写在了event包下,包括事件监听器(listener包),事件源(source包)和事件发布帮助器(helper包)。
  • controller和service和util包就是模拟了我们日常开发中常用三层分包模式,当然我这里没有涉及到dao层,我这里的事件就是源码分析的文章中最后给自己留家庭作业的事件,对应的监听器也是监听的家庭作业的监听器。
  • 下面的代码我都是截图的,为了防止正在看的你直接复制而不是自己亲自动手敲一遍,毕竟眼过千遍不如手过一遍嘛。

4.2 事件源

在这里插入图片描述

4.3 事件监听器

在这里插入图片描述

4.4 事件帮助器

4.4.1 事件发布帮助接口

在这里插入图片描述

4.4.2 事件发布帮助接口实现类

在这里插入图片描述

4.4.3 抽象的事件监听器

HomeworkListener继承了这个抽象的事件监听器
在这里插入图片描述

4.4.4 事件发布入口的Controller和Service

HomeworkController:
在这里插入图片描述
HomeworkService:
在这里插入图片描述

4.4.5 获取applicationContext对象工具类

在这里插入图片描述

4.4.5 启动Springboot后发送请求

直接用postman或者ApiFox调用Post接口:http://localhost:8081/homework/show
就可以看到控制台打印的日志了。

在这里插入图片描述
这里就是HomeworkServiceHomeworkEvent的事件内容被HomeworkListener接收到之后打印的信息日志了。
上午不知道咋操作的,想要重新启动Springboot发现端口总是被占用,哪怕我改了application.properties文件中的端口,改啥端口,啥端口就被占用,后面用下面的命令找到了被占用的端口,把这个端口杀掉就行。

sudo lsof -i tcp:8081

从列表中找到PID对应的进程id,执行kill pid就可以杀死这个该死的占用的进程。

五、事件发布机制原理

5.1 自己对事件发布机制的理解

看着上面控制台日志和我们直接在service中打印的一样,没有啥区别,但实际上这只是个例子,如果像Springboot中一样,有很多的事件,如果不采用事件发布机制,那发布事件之前的代码逻辑处理就很多,就使得原本复杂的逻辑更复杂了。
事件发布机制有点像我们列的待处理的工作清单一样,清单列好,再一件件的去完成,这样更加清晰明了,或者更专业的点的名称叫「解耦」,将业务和业务进行解耦。

5.2 事件发布机制源码分析

其实在SpringBoot源码分析(面试官:你说说Springboot的启动过程吧(5万字分析启动过程))中的A.5、发布启动的事件并由应用程序启动监听器进行处理B.6、发布上下文开始的事件都多多少少有提到过

这里结合我们自己的例子对源码进行分析,启动Springboot之后,发送Post请求,通过debug模式进入EventPublishHelperImpl中的applicationContext.publishEvent(event);的方法中一步步进入到AbstractApplicationContext中的publishEvent方法:
在这里插入图片描述
这个方法里面有3个if块,第一个if块必然走的是if分支,即HomeworkEventApplicationEvent的实例,第二个if块就是上图中断点的蓝色阴影的这行,因为earlyApplicationEvents是null。
而此时的断点中这行其实是先获取多播器,既然有获取,必然有设置,设置的方法我在源码分析的文章中的B.3.8、为此上下文初始化事件广播分析过initApplicationEventMulticaster方法,其实就是返回了默认的SimpleApplicationEventMulticaster进行广播处理。
我们进入到multicastEvent方法中,似乎看到了熟悉的方法,就是在A.5、发布启动的事件并由应用程序启动监听器进行处理也提到过。
在这里插入图片描述
我们通过分析器看到getApplicationListeners(event, type)里面有三个监听器,其中就包括我们定义的HomeworkListener,接着进入到了SimpleApplicationEventMulticaster.invokeListener方法中:
在这里插入图片描述
再进入到doInvokeListener方法中:
在这里插入图片描述
try-catch块中间的listener.onApplicationEvent(event);的listener就是我们的自定义的HomeworkListener监听器,就顺利成章的先进入到这个类的父类AbstractEventListener里,先打印了我们的接收的日志,再进入this.executeEvent(event);方法,此方法是个抽象方法,就进入了实现类HomeworkListener重写的此方法中了:
在这里插入图片描述
就执行了HomeworkListener类里的this.executeEvent方法了,所以分析下来其实并不难,但这种思想的来源却是需要多年的积累才能够进行提炼和实现的,这种思想其实就是设计模式中的观察者模式。感谢这些大佬的贡献,我们才能站在巨人的肩膀上走的更快。。。

---------------------你知道的越多,不知道的越多----------------------

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

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

相关文章

运维就业现状怎么样?技能要求高吗?

运维至少需要知道哪些知识才可以去面试&#xff1f; 有一个答案对这一话题的解读非常深入&#xff0c;体系化的分析了所需掌握的技术、工作内容、性质及可发展的方向等等&#xff0c;今天特别分享给大家&#xff0c;按照这个发展&#xff0c;运维则已不需担心就业、薪酬等问题了…

MySQL -2 指令

客户端SQL指令记录&#xff1a; -- 针对 数据库和针对数据表 &#xff08;一&#xff09;数据库 1. 查看当前所有数据库&#xff1a;show databases; 2. 创建数据库&#xff1a;create database 数据库名 DEFAULT CHARSET utf8 COLLATE utf8_general_ci; 3. 删除数据库&#…

【一建、一造经验分享】一建挺难的,要坚持才能得到

标签&#xff1a;【备考四年】、【2020全科通过一建】、【2021全科通过一造】 思绪回到2017年7月份&#xff0c;软考-项目管理师出成绩了&#xff0c;很幸运我通过了。由于通信行业及单位认可“以考代评”&#xff0c;所以我最先下手的是软考高级&#xff0c;拿证等同于高工。在…

程序员核心------详解调试(2)

所爱隔山海&#xff0c;山海皆可平&#xff0c;所念皆星河&#xff0c;星河不可及。 上课&#xff01; 接着上节课讲的调试&#xff08;1&#xff09;&#xff0c;本节课进一步讲解调试(2). 文章目录1.调试实例讲解&#xff08;2&#xff09;校招笔试题 2.如何写出好的&#…

【大数据技术Hadoop+Spark】MapReduce概要、思想、编程模型组件、工作原理详解(超详细)

MapReduce是Hadoop系统核心组件之一&#xff0c;它是一种可用于大数据并行处理的计算模型、框架和平台&#xff0c;主要解决海量数据的计算&#xff0c;是目前分布式计算模型中应用较为广泛的一种。 一、MapReduce核心思想 MapReduce的核心思想是“分而治之”。所谓“分而治之…

猿人学APP第一题

抓包分析 copy CURL 转 requests代码 def app1():import requestsheaders {"Accept-Language": "zh-CN,zh;q0.8","User-Agent": "Mozilla/5.0 (Linux; U; Android 8.1.0; zh-cn; Nexus 6P Build/OPM1.171019.011) AppleWebKit/534.30 (K…

KT6368A蓝牙芯片的MTU的说明以及如何加快BLE传输速率

目录 一、蓝牙MTU的简介 二、详细的方法说明以及测试 三、KT6368A提升ble传输速率方法 BLE传输带宽主要跟两个要素有关&#xff1a; 通信周期和每个通信点可传输的数据量。 1.通信周期&#xff1a; 安卓手机一般可支持到10ms&#xff0c;苹果一般可支持到15ms 2.每个通信点…

[附源码]Node.js计算机毕业设计高校实习管理平台系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

菁染料CY3-聚乙二醇-二苯并环辛炔 CY3-PEG-DBCO

产品名称&#xff1a;菁染料CY3-聚乙二醇-二苯并环辛炔 英文名称&#xff1a; CY3-PEG-DBCO 分子量&#xff1a;1k&#xff0c;2k&#xff0c;3.4k&#xff0c;5k&#xff0c;10k&#xff0c;20k&#xff08;可按需定制&#xff09; 质量控制&#xff1a;95% 原料分散系…

SSM校园疫情防控

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 管理员功能&#xff1a; 教职工管理&#xff08;管理已注册的教职工&#xff09; 出入管理&#xff08;出入日期、人员姓…

若依缓存使用浅析

配置 这块主要涉及两个类 FastJson2JsonRedisSerializer : 继承 RedisSerializer 接口自定义使用 fastjson 进行序列化和反序列化RedisConfig&#xff1a;配置使用 StringRedisSerializer 来进行key的序列化与反序列&#xff0c;使用刚才我们 FastJson2JsonRedisSerializer 来…

通过WSL2运行GUI程序

上次我写过一篇VGPU和WSL2&#xff0c;这回我打算写一篇关于《通过WSL2运行GUI程序》的笔记。 起因 总所周知&#xff0c;KVM in Linux支持GPU passthrough(GPU直通)功能的&#xff0c;就是配置稍稍多了一点。最初想试试如何在Hyper-V中尝试安装VM&#xff0c;但是vgpu的文档…

2022-12-14 jedis

jedis 简介 编程语言与redis java语言连接redis服务 jedis SpringDataRedis Lettuce HelloWorld 客户端连接redis maven下载安装 配置maven环境变量 库的种类和彼此的关系 maven标准目录结构 核心代码部分配置文件部分测试代码部分测试配置文件 maven常用的命令 mvn…

nodejs+vue083新生报到服务管理系统

目 录 摘 要 I 目 录 III 第一章 概述 1 1.1研究背景 1 1.2 开发意义 1 1.3 研究现状 1 1.4 研究内容 2 1.5 论文结构 2 第二章 开发技术介绍 1 2.5 B/S架构 2 第三章 系统分析 1 3.1 可行性分析 1 3.1.1技术可行性 1 3.1.2操作可…

python绘制圣诞树、烟花、爱心及节日倒计时

说明&#xff1a; 该博客主要完成以下几个方面的功能: 1.元旦节日倒计时代码的实现 2.使用python源码“绘制圣诞树” 3.使用python绘制“跨年烟花” 4.使用python 绘制“爱心” 呈现方式&#xff1a;代码和截图 1.元旦倒计时代码&#xff1a; 输入相应的节日时间&#…

Seata - @GlobalTransactional源码解析

脑图 核心 Seata三大角色 TC &#xff1a;事务协调者&#xff0c;netty server(服务器)TM &#xff1a;事务管理器&#xff0c;netty client(客户端)RM&#xff1a; 资源管理器&#xff0c;netty client(客户端) GlobalTransactional(name "fsp-create-order" ro…

【静脉检测】手指静脉图像检测【含Matlab源码 1654期】

⛄一、简介 手指静脉识别系统的性能非常依赖于采集图像的质量,但是采集设备在成像和传输时产生的各类噪声,以及开放式使用场景下设备镜面上存在脏污、用户手指存在蜕皮情况等因素都会对图像质量造成极大的影响,增大后续特征提取的难度,最终影响整个系统的识别性能。针对目前现…

Zabbix 6.2 监控 PostgreSQL13 数据库

Zabbix 6.2 监控 PostgreSQL13 数据库 文章目录Zabbix 6.2 监控 PostgreSQL13 数据库官方模版地址1.pgsql新建监控用户2. 编辑 pg_hba.conf 文件并重启3.拷贝监控脚本到var/lib/zabbix下4.zabbix监控导入模版5.主机配置模版6.验证监控数据官方模版地址 https://git.zabbix.com…

一文带你读懂何为 macOS App 公证,以及如何自动化实现

前言 在上篇文章「macOS App 自动化分发 App Store 探索与实践」中讲解了如何通过 Shell 脚本实现 macOS App 自动化分发 App Store。相信&#xff0c;看过的同学都或多或少对 macOS App 构建、分发 App Store 相关的知识都具备了一定的认知。 而对于开发者来说&#xff0c;我…

RCE(远程代码/命令执行漏洞)原理及靶场练习

目录 PHP-RCE涉及函数 基础命令符 靶场练习 PHP-RCE涉及函数 代码注入 eval() 把字符串 code 作为PHP代码执行 assert() 检查一个断言是否为 false preg_replace() 执行一个正则表达式的搜索和替换 create_function() 创建一个匿名函数并且返回函数名创 call_user_func()/ca…