SpringEvent事件发布订阅Demo

2024-06-01 18:36

本文主要是介绍SpringEvent事件发布订阅Demo,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 实现事件
  • 定义事件监听器
    • 方式一
    • 方式二
  • 定义事件发布者
    • 方式一
    • 方式二
  • 项目测试
  • 原理

本文参考:

基础用法:Spring Event事件发布&消费Demo - HumorChen99 - 博客园 (cnblogs.com)

比较全面的用法:Spring Event 事件发布/监听机制 详解并使用-CSDN博客

@EventListener 源码简单探究:SpringBoot中@EventListener注解的使用-CSDN博客

广播器与监听器初始化详细源码:深入理解Spring事件机制(一):广播器与监听器的初始化 - Createsequence - 博客园 (cnblogs.com)

事件如何广播推送的:[深入理解Spring事件机制(二):事件的推送通俗易懂]-腾讯云开发者社区-腾讯云 (tencent.com)

在看源码的过程中,看到大佬们通过 Spring Event 事件发布与订阅机制,都实现了比较优雅的代码,但是自己对于这方面的实践还是较少,因此这篇文章准备从 Demo 引入,了解相关的用法,后面再出文章进一步分析其中的原理。

实现Spring事件机制主要有4个类:

  1. ApplicationEvent:事件,每个实现类表示一类事件,可携带数据。
  2. ApplicationListener:事件监听器,用于接收事件处理时间。
  3. ApplicationEventMulticaster:事件管理者,用于事件监听器的注册和事件的广播。
  4. ApplicationEventPublisher:事件发布者,委托ApplicationEventMulticaster完成事件发布。

实现事件

自定义事件,继承 ApplicationEvent

package com.jxz;import lombok.Getter;
import lombok.ToString;
import org.springframework.context.ApplicationEvent;/*** @Author jiangxuzhao* @Description 自定义事件,继承 ApplicationEvent* @Date 2024/5/31*/
@Getter
@ToString
public class JxzEvent extends ApplicationEvent {private String jxzTopic;private String jxzContent;public JxzEvent(String topic, String content) {super(topic);this.jxzTopic = topic;this.jxzContent = content;}
}

定义事件监听器

方式一

自定义事件监听器,实现 ApplicationListener 接口,监听 JxzEvent 事件,同时别忘了将 JxzEventListener 通过 @Component 注入给 Spring IOC 容器

package com.jxz;import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;/*** @Author jiangxuzhao* @Description 自定义事件监听器,实现 ApplicationListener 接口,监听 com.jxz.JxzEvent 事件* @Date 2024/5/31*/
@Slf4j
@Component
public class JxzEventListener implements ApplicationListener<JxzEvent> {@Overridepublic void onApplicationEvent(JxzEvent event) {log.info("topic is = {}", event.getJxzTopic());log.info("content is = {}", event.getJxzContent());}
}

方式二

通过 @EventListener 注解,实现监听事件的效果

package com.jxz;import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;/*** @Author jiangxuzhao* @Description* @Date 2024/6/1*/
@Component
@Slf4j
public class JxzEventListener2 {@EventListener(value = {JxzEvent.class}) // @EventListener 注解public void listener(JxzEvent event) {log.info("topic is = {}", event.getJxzTopic());log.info("content is = {}", event.getJxzContent());}
}

定义事件发布者

方式一

需要引入 ApplicationEventPublisher 事件发布者,然后构造 JxzEvent 事件发布事件

package com.jxz;import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** @Author jiangxuzhao* @Description Controller 类方便测试* @Date 2024/5/31*/
@RestController
public class JxzController {/*** 需要引入 ApplicationEventPublisher 事件发布者*/@Resourceprivate ApplicationEventPublisher applicationEventPublisher;@GetMapping(path = "test/")public String test(String topic, String content) {// 通过传入参数构造 JxzEvent 事件JxzEvent jxzEvent = new JxzEvent(topic, content);// 发布事件applicationEventPublisher.publishEvent(jxzEvent);return jxzEvent.toString();}
}

方式二

Spring 中顶级的容器管理类 ApplicationContext

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver

extends 继承于 ApplicationEventPublisher,因此也可以通过注入父类的接口引入这个发布者

@Resource
private ApplicationContext applicationContext;@GetMapping(path = "/test2")
public String test2(String topic, String content) {// 通过传入参数构造 JxzEvent 事件JxzEvent jxzEvent = new JxzEvent(topic, content);// 发布事件applicationContext.publishEvent(jxzEvent);return jxzEvent.toString();
}

其实这两种方式都是通过接口的方式,自动装配了 AnnotationConfigServletWebServerApplicationContext 这个实现类。

项目测试

最后再给程序加一个启动类就可以测试了

package com.jxz;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** @Author jiangxuzhao* @Description* @Date 2024/5/31*/
@SpringBootApplication
public class SpringEventApplication {public static void main(String[] args) {SpringApplication.run(SpringEventApplication.class, args);}
}

启动这个类并加上 VM Option = -Dserver.port=8071,本地端口 8071 接收请求

http://localhost:8071/test?topic="jxztopic"&content="明天放假"

控制台输出如下,表明事件成功发出,监听者成功接收并打印内容

2024-05-31 23:59:23.930  INFO 45974 --- [nio-8071-exec-4] com.jxz.JxzEventListener                 : topic is = "jxztopic"
2024-05-31 23:59:23.932  INFO 45974 --- [nio-8071-exec-4] com.jxz.JxzEventListener                 : content is = "明天放假"

原理

原理这块还是值得细细看源码的,这里姑且做个引子,起个提纲挈领的作用,需要有些 Spring 源码的底子,小弟我也只能管中窥豹。

本质上就是使用到了观察者模式,之前也写过一篇文章讲过 设计模式-从回调函数入手理解观察者模式_回调函数与观察者模式-CSDN博客

监听器的逻辑:

SpringApplication#run -> AbstractApplicationContext#createApplicationContext() -> AnnotationConfigServletWebServerApplicationContext -> new AnnotatedBeanDefinitionReader -> AnnotationConfigUtils.registerAnnotationConfigProcessors -> new RootBeanDefinition(EventListenerMethodProcessor.class) -> AbstractApplicationContext#finishBeanFactoryInitialization -> beanFactory.preInstantiateSingletons() -> smartSingleton.afterSingletonsInstantiated() -> EventListenerMethodProcessor#afterSingletonsInstantiated -> EventListenerMethodProcessor#processBean -> context.addApplicationListener -> applicationEventMulticaster.addApplicationListener(listener) -> AbstractApplicationEventMulticaster#addApplicationListener -> this.defaultRetriever.applicationListeners.add(listener)

由于 EventListenerMethodProcessor 实现了 SmartInitializingSingleton 接口,因此在 beanFactory 结束初始化结束之后会被调用,最终调用到其afterSingletonsInstantiated 方法,在 processBean 方法中,会找出所有被@EventListener 注解的方法,将它们初始化成 ApplicationListener 对象,并最终放入 AbstractApplicationEventMulticaster#ListenerRetriever#applicationListeners 集合中,同时放入 AbstractApplicationContext#applicationListeners 集合中,这里就是完成了监听者的注册。

发布者逻辑:

ApplicationEventPublisher#publishEvent -> AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType) -> getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType) -> SimpleApplicationEventMulticaster#multicastEvent

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {Executor executor = getTaskExecutor();if (executor != null) {executor.execute(() -> invokeListener(listener, event));}else {invokeListener(listener, event);}}
}

其中 getApplicationListeners 方法就会从通过 retrieveApplicationListeners 从前面注册的 defaultRetriever.applicationListeners 里面拿到所有的监听者,同时配置了一个本地缓存 retrieverCache 来提速。最终调用 invokeListener -> doInvokeListener -> listener.onApplicationEvent(event) 就是成功调用了这些监听者的 onApplicationEvent 方法。

由此可见,观察者模式是通过同步的方式实现的事件通知,而非消息队列那样依赖消息中心实现的异步通知方式。

这篇关于SpringEvent事件发布订阅Demo的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1021792

相关文章

定价129元!支持双频 Wi-Fi 5的华为AX1路由器发布

《定价129元!支持双频Wi-Fi5的华为AX1路由器发布》华为上周推出了其最新的入门级Wi-Fi5路由器——华为路由AX1,建议零售价129元,这款路由器配置如何?详细请看下文介... 华为 Wi-Fi 5 路由 AX1 已正式开售,新品支持双频 1200 兆、配有四个千兆网口、提供可视化智能诊断功能,建

五大特性引领创新! 深度操作系统 deepin 25 Preview预览版发布

《五大特性引领创新!深度操作系统deepin25Preview预览版发布》今日,深度操作系统正式推出deepin25Preview版本,该版本集成了五大核心特性:磐石系统、全新DDE、Tr... 深度操作系统今日发布了 deepin 25 Preview,新版本囊括五大特性:磐石系统、全新 DDE、Tree

Linux Mint Xia 22.1重磅发布: 重要更新一览

《LinuxMintXia22.1重磅发布:重要更新一览》Beta版LinuxMint“Xia”22.1发布,新版本基于Ubuntu24.04,内核版本为Linux6.8,这... linux Mint 22.1「Xia」正式发布啦!这次更新带来了诸多优化和改进,进一步巩固了 Mint 在 Linux 桌面

多模块的springboot项目发布指定模块的脚本方式

《多模块的springboot项目发布指定模块的脚本方式》该文章主要介绍了如何在多模块的SpringBoot项目中发布指定模块的脚本,作者原先的脚本会清理并编译所有模块,导致发布时间过长,通过简化脚本... 目录多模块的springboot项目发布指定模块的脚本1、不计成本地全部发布2、指定模块发布总结多模

Python中的异步:async 和 await以及操作中的事件循环、回调和异常

《Python中的异步:async和await以及操作中的事件循环、回调和异常》在现代编程中,异步操作在处理I/O密集型任务时,可以显著提高程序的性能和响应速度,Python提供了asyn... 目录引言什么是异步操作?python 中的异步编程基础async 和 await 关键字asyncio 模块理论

禁止平板,iPad长按弹出默认菜单事件

通过监控按下抬起时间差来禁止弹出事件,把以下代码写在要禁止的页面的页面加载事件里面即可     var date;document.addEventListener('touchstart', event => {date = new Date().getTime();});document.addEventListener('touchend', event => {if (new

高效+灵活,万博智云全球发布AWS无代理跨云容灾方案!

摘要 近日,万博智云推出了基于AWS的无代理跨云容灾解决方案,并与拉丁美洲,中东,亚洲的合作伙伴面向全球开展了联合发布。这一方案以AWS应用环境为基础,将HyperBDR平台的高效、灵活和成本效益优势与无代理功能相结合,为全球企业带来实现了更便捷、经济的数据保护。 一、全球联合发布 9月2日,万博智云CEO Michael Wong在线上平台发布AWS无代理跨云容灾解决方案的阐述视频,介绍了

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧

maven发布项目到私服-snapshot快照库和release发布库的区别和作用及maven常用命令

maven发布项目到私服-snapshot快照库和release发布库的区别和作用及maven常用命令 在日常的工作中由于各种原因,会出现这样一种情况,某些项目并没有打包至mvnrepository。如果采用原始直接打包放到lib目录的方式进行处理,便对项目的管理带来一些不必要的麻烦。例如版本升级后需要重新打包并,替换原有jar包等等一些额外的工作量和麻烦。为了避免这些不必要的麻烦,通常我们

禅道Docker安装包发布

禅道Docker安装包发布 大家好, 禅道Docker安装包发布。 一、下载地址 禅道开源版:   /dl/zentao/docker/docker_zentao.zip  备用下载地址:https://download.csdn.net/download/u013490585/16271485 数据库用户名: root,默认密码: 123456。运行时,可以设置 MYSQL_ROOT_P