Spring Event

news2024/11/20 19:48:02
  • 前言

  • ApplicationEvent 与 ApplicationListener 应用

    • 实现

    • 基于注解

    • 事件过滤

    • 异步事件监听

  • 好处及应用场景

  • 源码阅读

  • 总结

1前言

ApplicationContext 中的事件处理是通过 ApplicationEvent 类和 ApplicationListener 接口提供的。如果将实现了 ApplicationListener 接口的 bean 部署到容器中,则每次将 ApplicationEvent 发布到ApplicationContext 时,都会通知到该 bean,这简直是典型的观察者模式。设计的初衷就是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。

Spring 中提供了以下的事件

图片

2ApplicationEvent 与 ApplicationListener 应用

实现

1、自定义事件类,基于 ApplicationEvent 实现扩展

public class DemoEvent extends ApplicationEvent {
    private static final long serialVersionUID = -2753705718295396328L;
    private String msg;

    public DemoEvent(Object source, String msg) {
        super(source);
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

2、定义 Listener 类,实现 ApplicationListener接口,并且注入到 IOC 中。等发布者发布事件时,都会通知到这个bean,从而达到监听的效果。

@Component
public class DemoListener implements ApplicationListener<DemoEvent> {
    @Override
    public void onApplicationEvent(DemoEvent demoEvent) {
        String msg = demoEvent.getMsg();
        System.out.println("bean-listener 收到了 publisher 发布的消息: " + msg);
    }
}

3、要发布上述自定义的 event,需要调用 ApplicationEventPublisher 的 publishEvent 方法,我们可以定义一个实现 ApplicationEventPublisherAware 的类,并注入 IOC来进行调用。

@Component
public class DemoPublisher implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void sendMsg(String msg) {
        applicationEventPublisher.publishEvent(new DemoEvent(this, msg));
    }
}

4、客户端调用 publisher

@RestController
@RequestMapping("/event")
public class DemoClient implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @GetMapping("/publish")
    public void publish(){
        DemoPublisher bean = applicationContext.getBean(DemoPublisher.class);
        bean.sendMsg("发布者发送消息......");
    }
}

输出结果:

bean-listener 收到了 publisher 发布的消息: 发布者发送消息......

基于注解

我们可以不用实现 AppplicationListener 接口 ,在方法上使用 @EventListener 注册事件。如果你的方法应该侦听多个事件,并不使用任何参数来定义,可以在 @EventListener 注解上指定多个事件。

重写 DemoListener 类如下:

public class DemoListener {
    @EventListener(value = {DemoEvent.class, TestEvent.class})
    public void processApplicationEvent(DemoEvent event) {
        String msg = event.getMsg();
        System.out.println("bean-listener 收到了 publisher 发布的消息: " + msg);
    }
}
事件过滤

如果希望通过一定的条件对事件进行过滤,可以使用 @EventListener 的 condition 属性。以下实例中只有 event 的 msg 属性是 my-event 时才会进行调用。

@EventListener(value = {DemoEvent.class, TestEvent.class}, condition = "#event.msg == 'my-event'")
public void processApplicationEvent(DemoEvent event) {
     String msg = event.getMsg();
     System.out.println("bean-listener 收到了 publisher 发布的消息: " + msg);
 }

此时,发送符合条件的消息,listener 才会侦听到 publisher 发布的消息。

bean-listener 收到了 publisher 发布的消息: my-event

异步事件监听

前面提到的都是同步处理事件,那如果我们希望某个特定的侦听器异步去处理事件,如何做?

使用 @Async 注解可以实现类内方法的异步调用,这样方法在执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作。

@EventListener
@Async
public void processApplicationEvent(DemoEvent event) {
    String msg = event.getMsg();
    System.out.println("bean-listener 收到了 publisher 发布的消息: " + msg);
}

使用异步监听时,有两点需要注意:

  • 如果异步事件抛出异常,则不会将其传播到调用方。

  • 异步事件监听方法无法通过返回值来发布后续事件,如果需要作为处理结果发布另一个事件,请插入 ApplicationEventPublisher 以手动发布事件

3好处及应用场景

ApplicationContext 在运行期会自动检测到所有实现了 ApplicationListener 的 bean,并将其作为事件接收对象。当我们与 spring 上下文交互触发 publishEvent 方法时,每个实现了 ApplicationListener 的 bean 都会收到 ApplicationEvent 对象,每个 ApplicationListener 可以根据需要只接收自己感兴趣的事件。

这样做有什么好处呢?

在传统的项目中,各个业务逻辑之间耦合性比较强,controller 和 service 间都是关联关系,然而,使用 ApplicationEvent 监听 publisher 这种方式,类间关系是什么样的?我们不如画张图来看看。

DemoPublisher 和 DemoListener 两个类间并没有直接关联,解除了传统业务逻辑两个类间的关联关系,将耦合降到最小。这样在后期更新、维护时难度大大降低了。

图片

ApplicationEvent 使用观察者模式实现,那什么时候适合使用观察者模式呢?观察者模式也叫 发布-订阅模式,例如,微博的订阅,我们订阅了某些微博账号,当这些账号发布消息时,我们都会收到通知。

总结来说,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,从而实现广播的效果。

4源码阅读

图片

Spring中的事件机制流程

1、ApplicationEventPublisher是Spring的事件发布接口,事件源通过该接口的pulishEvent方法发布事件

2、ApplicationEventMulticaster就是Spring事件机制中的事件广播器,它默认提供一个SimpleApplicationEventMulticaster实现,如果用户没有自定义广播器,则使用默认的。它通过父类AbstractApplicationEventMulticastergetApplicationListeners方法从事件注册表(事件-监听器关系保存)中获取事件监听器,并且通过invokeListener方法执行监听器的具体逻辑

3、ApplicationListener就是Spring的事件监听器接口,所有的监听器都实现该接口,本图中列出了典型的几个子类。其中RestartApplicationListnener在SpringBoot的启动框架中就有使用

4、在Spring中通常是ApplicationContext本身担任监听器注册表的角色,在其子类AbstractApplicationContext中就聚合了事件广播器ApplicationEventMulticaster和事件监听器ApplicationListnener,并且提供注册监听器的addApplicationListnener方法

ChatGPT中文网站:https://ai.cxyquan.com/   

通过上图就能较清晰的知道当一个事件源产生事件时,它通过事件发布器ApplicationEventPublisher发布事件,然后事件广播器ApplicationEventMulticaster会去事件注册表ApplicationContext中找到事件监听器ApplicationListnener,并且逐个执行监听器的onApplicationEvent方法,从而完成事件监听器的逻辑。

来到ApplicationEventPublisher 的 publishEvent 方法内部

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
 if (this.earlyApplicationEvents != null) {
 this.earlyApplicationEvents.add(applicationEvent);
 }
 else {
  // 
  getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
 }
}

多播事件方法

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
 ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
 Executor executor = getTaskExecutor();
 // 遍历所有的监听者
 for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
  if (executor != null) {
   // 异步调用监听器
   executor.execute(() -> invokeListener(listener, event));
  }
  else {
   // 同步调用监听器
   invokeListener(listener, event);
  }
 }
}

invokeListener

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
 ErrorHandler errorHandler = getErrorHandler();
 if (errorHandler != null) {
  try {
   doInvokeListener(listener, event);
  }
  catch (Throwable err) {
   errorHandler.handleError(err);
  }
 }
 else {
  doInvokeListener(listener, event);
 }
}

doInvokeListener

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
 try {
  // 这里是事件发生的地方
  listener.onApplicationEvent(event);
 }
 catch (ClassCastException ex) {
  ......
 }
}

点击 ApplicationListener 接口 onApplicationEvent 方法的实现,可以看到我们重写的方法。

图片

5总结

Spring 使用反射机制,获取了所有继承 ApplicationListener 接口的监听器,在 Spring 初始化时,会把监听器都自动注册到注册表中。

Spring 的事件发布非常简单,我们来总结一下:

  • 定义一个继承 ApplicationEvent 的事件

  • 定义一个实现 ApplicationListener 的监听器或者使用 @EventListener 监听事件

  • 定义一个发送者,调用 ApplicationContext 直接发布或者使用 ApplicationEventPublisher 来发布自定义事件

最后,发布-订阅模式可以很好的将业务逻辑进行解耦(上图验证过),大大提高了可维护性、可扩展性。

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

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

相关文章

重磅|博睿数据 Bonree ONE 2023秋季版焕新发布!

2023年10月20日&#xff0c;以「数智融&#xff0c;ONE向新」为主题的 Bonree ONE 秋季产品发布会在深圳圆满落幕。此次发布会上&#xff0c;博睿数据隆重发布新一代一体化智能可观测平台——Bonree ONE秋季正式版&#xff0c;重点升级数据采集、全局拓扑、数据分析、会话回放等…

vue 使用$router.push(参数)跳转同一路由页面,参数不同,跳转页面数据均为最后一次传值数据

问题描述&#xff1a;项目中&#xff0c;使用$router.push(参数)跳转同一路由页面&#xff0c;打开多个tab&#xff0c;数据均为最后一次打开的数据&#xff1b; 1、列表页面 2、从1进入的编辑详情&#xff1a; 3、从2进入的编辑详情&#xff1a; 4、此时1的编辑详情内容也变…

第二届、第三届<火焰杯>软件测试开发选拔赛河北赛区颁奖典礼落幕

河北新闻网讯&#xff08;燕赵都市报纵览新闻记者 张超&#xff09;3月25日下午&#xff0c;第二届、第三届<火焰杯>软件测试开发选拔赛河北赛区颁奖典礼在河北工程技术学院图书馆报告厅隆重举行。 出席典礼的领导及嘉宾有&#xff1a;河北工程技术学院副校长田光、教学…

常用Web安全扫描工具汇整

漏洞扫描是一种安全检测行为&#xff0c;更是一类重要的网络安全技术&#xff0c;它能够有效提高网络的安全性&#xff0c;而且漏洞扫描属于主动的防范措施&#xff0c;可以很好地避免黑客攻击行为&#xff0c;做到防患于未然。 1、AWVS Acunetix Web Vulnerability Scanner&a…

中国大模型开源创新与合作的新篇章 | 2023 CCF中国开源大会

2023年10月21日至22日&#xff0c;由中国计算机学会&#xff08;CCF&#xff09;和开放原子开源基金会联合主办的CCF中国开源大会&#xff08;CCF ChinaOSC&#xff09;在湖南省长沙市北辰国际会议中心成功召开。此次大会以“开源联合&#xff0c;聚力共赢”为主题&#xff0c;…

硬件信息查看工具 EtreCheckpro mac中文版功能介绍

etrecheckpro mac中文版是一款专业的硬件信息查看工具&#xff0c;它能够快速的检测Mac电脑的软硬件信息&#xff0c;加强用户对自己计算机的了解&#xff0c;EtreCheckPro for Mac下载首先会对电脑的软硬件信息进行扫描收集&#xff0c;之后才会显示出来。EtreCheck Mac版报告…

数据结构 哈希表

数据结构 哈希表 文章目录 数据结构 哈希表1. 概念2. 冲突-概念3. 冲突-避免3.1 哈希函数设计3.2 负载因子调节 4.冲突-解决4.1 闭散列4.2 开散列(哈希桶)4.3 哈希桶实现 5. 性能分析6. 和java类集的关系 1. 概念 顺序结构以及平衡树中&#xff0c;元素关键码与其存储位置之间…

k8s----11、service

services 1、概述2、存在的意义2.1 服务发现2.2 负载均衡 3、pod与service的关系4、service 三种类型4.1 、 ClusterIP4.2 、NodePort4.3 、LoadBalancer 1、概述 Service 是 Kubernetes 最核心概念&#xff0c;通过创建 Service,可以为一组具有相同功能的容器应 用提供一个统…

说说腾讯云新用户那些事以及产品首单说明

腾讯云新用户是什么意思&#xff1f;新用户是指从未在腾讯云购买过云产品的用户&#xff0c;新用户可以购买特价服务器你不会不知道吧&#xff1f;一定要珍惜你的新用户资格&#xff0c;因为腾讯云很多优惠价格都是新用户专享的。腾讯云百科txybk.com来详细说下什么是腾讯云新用…

Windows安装cygwin + swoole,并配置crontab定时任务

一、Windows安装cygwin 安装包下载 自行搜索安装包进行下载&#xff0c;目前官网无法打开。官网地址&#xff1a;http://www.cygwin.com/ cygwin安装详细教程 1. 打开安装包&#xff1a;setup-x86_64.exe 2. 点击“下一页”&#xff0c;选择第一种方式后再点击“下一页” 3…

NFTScan | 10.16~10.22 NFT 市场热点汇总

欢迎来到由 NFT 基础设施 NFTScan 出品的 NFT 生态热点事件每周汇总。 周期&#xff1a;2023.10.16~ 2023.10.22 NFT Hot News 01/y00ts&#xff1a;迁移回以太坊的跨链桥已上线&#xff0c;将承担第一天所有 Gas 费 10 月 16 日&#xff0c;y00ts 发推称&#xff0c;将 y00…

Etsy店铺防防关联,自养号防关联环境搭建要点

Etsy是美国一个在线销售手工工艺品的网站&#xff0c;主要为客户提供定制化的手工制品和古董收藏品等。同时&#xff0c;该平台还为设计师、工匠和买家提供了一个可靠的交易平台。 在Etsy平台上&#xff0c;用户可以开设店铺&#xff0c;销售各类产品&#xff0c;包括摄影、艺…

halcon 多车牌识别

文章目录 车牌定位字符分割字符识别多张图片 车牌定位 dev_close_window() dev_open_window(0,0,512,400,black,WindowHandle) list_image_files(C:/Users/Augustine/Desktop/车牌照图片,jpg,[],ImageFiles) for i:0 to |ImageFiles|-1 by 1read_image(Image, ImageFiles[i])*…

GoLong的学习之路(一)语法之变量与常量

目录 GoLang变量批量声明变量的初始化类型推导短变量声明匿名变量 常量iota&#xff08;特殊&#xff09;&#xff08;需要重点记忆&#xff09; GoLang go的诞生为了解决在21世纪多核和网络化环境越来越复杂的变成问题而发明的Go语言。 go语言是从Ken Thomepson发明的B语言和…

RPA对财务报表的影响

在企业的财务管理中&#xff0c;财务报表分析能够帮助管理人员及时掌握企业的生产经营状况与财务状况&#xff0c;并为管理人员的科学决策提供财务数据支撑&#xff0c;规避企业经营风险。然而&#xff0c;传统的财务报表准备过程通常是繁琐、耗时和容易出错的&#xff0c;RPA技…

如何给R128在FreeRTOS下配置/data目录

在调试音频、usb等模块时&#xff0c;会发现SDK的根目录下没有/data该目录&#xff0c;导致无法存储所需要的文件&#xff0c;这就是因为/data目录没有配置好的原因。 1、选上配置 首先需要选上的配置&#xff1a; 运行mrtos_menuconfig&#xff0c;选上COMPONENT_LITTLEFS …

IDC报告:腾讯云领跑工业云市场,装备制造领域位列行业第一

10月23日&#xff0c;全球领先的IT市场研究和咨询公司IDC发布了《中国工业云IaaSPaaS市场份额&#xff0c;2022&#xff1a;分化初现》报告&#xff0c;腾讯云凭借领先的技术优势和助力实体经济数字化转型成果&#xff0c;成为头部厂商中增速最快的云计算厂商。在装备制造领域&…

服务器数据恢复-raid0硬盘故障导致服务器崩溃的数据恢复案例

服务器故障&分析&#xff1a; 某公司一台服务器&#xff0c;共2块磁盘组建了一组raid0磁盘阵列。RAID0阵列中有1块硬盘出现故障离线&#xff0c;导致服务器崩溃。 通过和用户方的沟通&#xff0c;北亚企安数据恢复工程师初步判断&#xff1a;故障服务器中出现故障离线的硬盘…

为什么学完了 C#觉得自己什么都干不了?

为什么学完了 C#觉得自己什么都干不了&#xff1f; 俺一向的观点&#xff1a;&#xff08;1&#xff09;学跟干是两码事。学&#xff0c;你要往外掏钱或时间或两个都得掏。干&#xff0c;是你从别人兜里掏钱。&#xff08;2&#xff09;如果没有干的需求&#xff0c;那么可以啥…

软件外包开发迭代管理工具

软件迭代的管理工具有助于团队有效地规划、跟踪和管理迭代开发过程&#xff0c;确保项目按时交付&#xff0c;并与团队成员之间进行协作。以下是一些常用的软件迭代管理工具&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#…