当前位置: 首页 > news >正文

Spring实例化源码解析之Custom Events下集(九)

上集从官网的角度讲解了基本的使用和源码的内容,没有深入的进行分析,本章将从源码的角度分析ApplicationEvent、ApplicationListener、ApplicationEventMulticaster这三者之间的关系。

initApplicationEventMulticaster

上一章后续部分给出了源码的含义,我们从中可以知道默认的情况下,也就是我们BeanFactory中没有存在名称为applicationEventMulticaster的BeanDefinition或者Bean,是会创建一个SimpleApplicationEventMulticaster应用事件广播器,也就是else中的逻辑,创建一个然后注册到Beanfactory中。

protected void initApplicationEventMulticaster() {ConfigurableListableBeanFactory beanFactory = getBeanFactory();if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {this.applicationEventMulticaster =beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);if (logger.isTraceEnabled()) {logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");}}else {// 广播器的创建SimpleApplicationEventMulticasterthis.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);if (logger.isTraceEnabled()) {logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");}}}

只有自定义一个应用事件广播器的时候会走上面的if中的逻辑。我这边直接改造一下这个SimpleApplicationEventMulticaster的实现,如下所示:

@Configuration
public class CustomApplicationEventMulticasterConfig {@Beanpublic ApplicationEventMulticaster applicationEventMulticaster(){SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();return simpleApplicationEventMulticaster;}
}

默认的SimpleApplicaitonEventMulticaster是不支持异步的,所以我们可以配置线程池以支持异步事件处理。

通过配置一个合适的线程池,你可以使事件的处理在独立的线程中进行,从而实现异步处理,避免阻塞主线程。

@Configuration
public class CustomApplicationEventMulticasterConfig {@Beanpublic TaskExecutor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(10); // 设置核心线程数executor.setMaxPoolSize(50); // 设置最大线程数executor.setQueueCapacity(100); // 设置队列容量executor.setThreadNamePrefix("event-executor-"); // 设置线程名称前缀executor.initialize();return executor;}@Beanpublic ApplicationEventMulticaster applicationEventMulticaster(TaskExecutor taskExecutor) {SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();eventMulticaster.setTaskExecutor(taskExecutor); // 设置线程池return eventMulticaster;}
}

再看我们的EmailService类,可以通过AnnotationConfigApplicationContext也就是ApplicationEventPublisher执行publishEvent广播事件。我们这儿只传递了一个参数,也就是ApplicationEvent event参数。

@Component
public class EmailService implements ApplicationEventPublisherAware, ApplicationContextAware {// 使用ApplicationEventPublisher应用事件发布器发布事件private ApplicationEventPublisher applicationEventPublisher;private ApplicationContext applicationContext;@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}public void sendEmail(String address, String content) {applicationEventPublisher.publishEvent(new BlockedListEvent(applicationContext, address, content));}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext=applicationContext;}
}

在SimpleApplicationEventMulticaster的multicastEvnet方法中会执行getApplicationListeners(event, type)方法,通过event去获取监听event的监听器。

@Overridepublic 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);}}}

最终会使用监听器调用onApplicationEvent方法来达到通知和广播的目的。如下图所示:

在这里插入图片描述

总结:ApplicationEvent是一种应用事件,ApplicationListener就是和事件进行绑定,当有相应的应用事件广播的时候,就会找到所有和应用事件绑定的监听器,调用其onApplicationEvent方法。而ApplicationEventMulticaster是什么?个人感觉简单来说它就是一个“路由器”。

Annotation-based Event Listeners

案例一

可以使用@EventListener注解将事件监听器注册到托管bean的任何方法上。AnnotationBlockedListNotifier可以按以下方式进行重写:

@Component
public class AnnotationBlockedListNotifier {@EventListener// 这个名字随便写都行public void processBlockedListEvent(BlockedListEvent event) {System.out.println("Annotation-地址:"+event.getAddress());System.out.println("Annotation-内容:"+event.getContent());}
}

我们使用@EventListener注解将processBlockedListEvent方法标记为BlockedListEvent的事件监听器。当发布一个BlockedListEvent事件时,Spring会自动调用该方法,并将事件作为参数传递给它。

使用@EventListener注解可以简化事件监听器的注册过程,无需实现ApplicationListener接口或显式注册为Spring bean。Spring会自动扫描并识别带有@EventListener注解的方法,并将其注册为事件监听器。

请注意,使用@EventListener注解的方法可以有不同的访问修饰符(public、protected、private等),并且可以带有其他参数。Spring将根据参数类型进行事件匹配,并将事件作为方法的参数传递。

案例二

方法签名再次声明了它所监听的事件类型,但这次使用了灵活的名称,并且无需实现特定的监听器接口。只要实际的事件类型在其实现层次结构中解析了我们的泛型参数,事件类型也可以通过泛型进行缩小。

如果我们的方法应该监听多个事件,或者如果我们希望在没有参数的情况下定义它,事件类型也可以在注解本身上指定。以下示例展示了如何实现:

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;@Component
public class MyEventListener {@EventListener({EventA.class, EventB.class})public void onEvent(Object event) {// 处理事件逻辑System.out.println("Received event: " + event);}
}

在上述示例中,我们使用@EventListener注解并在注解上指定了要监听的事件类型(EventA和EventB)。在方法中,我们将事件类型声明为Object类型,以接收不同类型的事件。在事件处理逻辑中,我们可以根据实际的事件类型进行处理。

请注意,使用@EventListener注解的方法可以具有不同的访问修饰符(public、protected、private等),并且可以带有其他参数。Spring将根据方法的参数类型和注解上指定的事件类型进行事件匹配。

案例三

还可以通过在定义注解的条件属性中使用SpEL表达式来添加额外的运行时过滤,以匹配特定事件才实际调用方法。

以下示例展示了如何重写我们的通知器,只有当事件的content属性等于"my-event"时才会被调用:

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;@Component
public class BlockedListNotifier {@EventListener(condition = "#event.content == 'my-event'")public void onBlockedListEvent(BlockedListEvent event) {// 处理事件逻辑System.out.println("Received blocked list event: " + event.getMessage());}
}

在上述示例中,我们使用@EventListener注解并在注解的条件属性上指定了一个SpEL表达式("#event.content == ‘my-event’)。这个表达式将用于过滤事件,只有当事件的content属性等于"my-event"时才会调用方法。

通过使用条件属性,我们可以根据事件的属性值或其他条件灵活地控制方法的调用。在SpEL表达式中,可以使用事件对象的属性和方法进行比较、计算和判断。

该功能不支持异步监听器!

Asynchronous Listeners

如果希望特定的监听器以异步方式处理事件,可以重用常规的@Async支持。以下示例展示了如何实现:

import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;@Component
public class MyAsyncEventListener {@Async@EventListenerpublic void onEvent(Object event) {// 异步处理事件逻辑System.out.println("Received event asynchronously: " + event);}
}

在上述示例中,我们在方法上使用了@Async注解来表示该方法应以异步方式执行。同时,我们仍然使用@EventListener注解将该方法标记为事件监听器。当事件发生时,Spring将自动使用异步线程池来执行该方法,从而实现异步事件处理。

要使用@Async注解,我们还需要在Spring配置中启用异步支持。可以通过在配置类上添加@EnableAsync注解来实现。

通过将@Async注解与@EventListener注解结合使用,可以实现异步事件处理。这使我们能够在后台线程中并行处理事件,从而提高系统的响应性和性能。请注意,异步事件处理可能会导致事件处理的顺序变得不确定,因此请谨慎使用。

Be aware of the following limitations when using asynchronous events:

  • If an asynchronous event listener throws an Exception, it is not propagated to the caller. See AsyncUncaughtExceptionHandler for more details.
  • Asynchronous event listener methods cannot publish a subsequent event by returning a value. If you need to publish another event as the result of the processing, inject an ApplicationEventPublisher to publish the event manually.

在使用异步事件时,请注意以下限制:

如果异步事件监听器抛出异常,它不会传播到调用方。有关更多详细信息,请参阅AsyncUncaughtExceptionHandler。

异步事件监听器方法无法通过返回值发布后续事件。如果需要在处理的结果中发布另一个事件,请注入ApplicationEventPublisher以手动发布事件。

Ordering Listeners

如果您需要一个监听器在另一个监听器之前被调用,您可以在方法声明中添加@Order注解,如下面的示例所示:

import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;@Component
public class MyEventListener {@Order(1)@EventListenerpublic void firstListener(MyEvent event) {// 第一个监听器的逻辑}@Order(2)@EventListenerpublic void secondListener(MyEvent event) {// 第二个监听器的逻辑}
}

在上述示例中,我们使用@Order注解来指定监听器方法的执行顺序。在第一个监听器方法上,我们将@Order(1)注解添加,表示它应该在第二个监听器方法之前被调用。在第二个监听器方法上,我们将@Order(2)注解添加,表示它应该在第一个监听器方法之后被调用。

请注意,@Order注解的值越小,优先级越高。因此,具有较小@Order值的监听器将在具有较大@Order值的监听器之前被调用。

通过使用@Order注解,您可以控制监听器方法的执行顺序,以确保它们按照您的需求进行调用。

总结

第八章和这一章节主要分析和描述了Spring事件的使用,在日常工作中基本没怎么使用,本来想绕过这一段直接进行初始化实例化和三级缓存,抱着学习的态度还是仔细分析了一下,然后查阅了官网进行学习,还是有所收获,希望对正在学习的朋友们有所帮助。

相关文章:

Spring实例化源码解析之Custom Events下集(九)

上集从官网的角度讲解了基本的使用和源码的内容&#xff0c;没有深入的进行分析&#xff0c;本章将从源码的角度分析ApplicationEvent、ApplicationListener、ApplicationEventMulticaster这三者之间的关系。 initApplicationEventMulticaster 上一章后续部分给出了源码的含义…...

python numpy库关键函数说明

python numpy库函数说明 np.argwhere()np.dtype()np.shape()np.zeros() np.argwhere() 输入参数是一个基本的逻辑表达式&#xff0c;输出检索结果的索引值。 >>> x np.arange(6).reshape(2,3) >>> x array([[0, 1, 2],[3, 4, 5]]) >>> np.argwhe…...

【Linux C】Linux如何执行一个程序(程序存储空间、系统调用、内核调用)

文章目录 一、程序存储空间1.1 C语言程序存储空间1.2 用户空间和内核空间1.3 用户模式和内核模式 二、内核调用-系统调用-C语言库函数2.1 系统调用和内核调用2.2 C语言库函数 三、Linux如何执行一个程序 一、程序存储空间 本节说的空间主要是指内存空间&#xff0c;即程序如何分…...

IP协议总结

一、定义。 IP全称为Internet Protocol&#xff0c;是TCP/IP协议族中的一员&#xff0c;负责实现数据在网络上的传输。它是一种无连接、不可靠的数据报协议。 IP协议常用于Internet网络和局域网中&#xff0c;它通过将数据包进行分组并进行逐跳转发来实现数据在网络中的传输。…...

微信支付v2

文档&#xff1a; https://pay.weixin.qq.com/wiki/doc/api/index.html 微信小程序&#xff1a;https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter11_1 需要一个微信认证后的小程序&#xff0c;&#xff0c;还需要一个&#xff0c;在微信商户平台&#xff0c;&…...

tcpdump(二)命令行参数讲解(一)

一 tcpdump实战详解 1、我们做抓包,一般都需要指定条件,保证对系统的CPU、内存、磁盘资源不会产生过大的响应备注&#xff1a; 遇到过tcpdump持续抓包导致系统挂了2、条件&#xff1a;1) tcpdump的 基础命令选项参数2) 真正的 过滤条件 抓包工具tcpdump用法说明 ① 参数学…...

10_8C++

X-Mind #include <iostream>using namespace std; class Rect { private:int width;int heigjt; public:void init(int w,int h){width w;heigjt h;}void set_w(int w){width w;}void set_h(int h){heigjt h;}void show(){cout << "矩形的周长" <…...

JVM篇---第七篇

系列文章目录 文章目录 系列文章目录一、Minor GC与Full GC分别在什么时候发生?二、你知道哪些JVM性能调优参数?(简单版回答)三、对象一定分配在堆中吗?有没有了解逃逸分析技术?一、Minor GC与Full GC分别在什么时候发生? 新生代内存不够用时候发生MGC也叫YGC,JVM内存…...

更新Xcode 版本后运行项目出现错误 Unable to boot the Simulator 解决方法

错误截图 出现 Unable to boot the Simulator 错误原因很多&#xff0c;以下方法不一定都适用&#xff0c;我是通过以下方法解决的 打开命令终端输入以下命令&#xff0c;可能需要你输入开机密码 sudo rm -rf ~/Library/Developer/CoreSimulator/Caches...

winform窗体控件太多显示不过来,怎么实现滚动条

winform窗体控件太多显示不过来&#xff0c;怎么实现滚动条 Winform Panel实现滚动条 一、创建panel 在界面上拖拽一个父级Panel1&#xff0c;然后在Panel1里面拖拽一个子级Panel2 设置父级Panel1的AutoScroll属性为True 属性设置好后&#xff0c;当子级高度或者宽度大于父…...

WebSocket连接异常 Error parsing HTTP request header Connection reset by peer

问题描述 在使用spring的方式集成websocket时&#xff0c;在配置WebSocketConfigurer后 Configuration EnableWebSocket public class WebSocketConfiguration implements WebSocketConfigurer {ResourceServletWebSocketServerHandler servletWebSocketServerHandler;Overri…...

Spring中shutdown hook作用

在Spring框架中&#xff0c;Shutdown Hook&#xff08;关闭钩子&#xff09;是一种机制&#xff0c;用于在应用程序关闭时执行一些清理操作Spring会向JVM注册一个shutdown hook&#xff0c;在接收到关闭通知的时候&#xff0c;进行bean的销毁&#xff0c;容器的销毁处理等操作在…...

关于IvorySQL和OpenGauss包SPEC处理的一些思考

包的SPEC区可以定义下面三种类型&#xff08;本篇只讨论SPEC区的情况&#xff09; 变量类型&#xff08;nested table等&#xff09;&#xff08;注意这是包内定义的类型&#xff0c;与SQL创建的不通&#xff09;游标 这三种类型在PG原生中&#xff0c;是找不到相似的功能的&…...

我用PYQT5做的第一个实用的上位机项目(六)

将之前的画面和代码用复制粘贴的方法复制四份&#xff0c;就完成了整个主画面和主程序的基本构建。 下面的工作是关于PLC和通信。 上位机项目&#xff0c;其与PLC通信的模式很多都是这样的&#xff1a;在没有操作和设置的平常显示界面&#xff0c;按照预定周期从PLC读取当前页…...

【高级语言程序设计】python函数式编程(一)

基础知识 Python函数式编程的主要内容包括以下几个方面&#xff1a; (1)函数作为一等公民&#xff1a;在函数式编程中&#xff0c;函数被视为一等公民&#xff0c;可以像其他数据类型一样被传递、赋值以及作为返回值。 (2)不可变数据&#xff1a;函数式编程鼓励使用不可变数据…...

使用python查找指定文件夹下所有xml文件中带有指定字符的xml文件

文件夹目录如下&#xff08;需要递归删除文件夹下的.DS_Store文件&#xff09;&#xff1a; labels文件夹下面是xml文件&#xff1a; import os import os.pathpath "name/labels" files os.listdir(path) # 得到文件夹下所有文件名称 s []for xmlFile in files:…...

flutter实现透明appbar(一)

前言 在项目中如何实现透明的appbar&#xff0c;方式一&#xff1a; 使用stack和positioned定位功能把appbar定位到页面的最上面&#xff0c; 实现 实现 Widget build(BuildContext context) {return Scaffold(body: Stack(children: [_homePage(), _appBar()],),);}_appbar…...

(四)正点原子STM32MP135移植——u-boot移植

一、概述 u-boot概述就不概述了&#xff0c;u-boot、kernel、dtb三件套&#xff0c;dddd 经过国庆艰苦奋战&#xff0c;已经成功把所有功能移植好了 二、编译官方代码 进入u-boot的目录 2.1 解压源码、打补丁 /* 解压源码 */ tar xf u-boot-stm32mp-v2022.10-stm32mp-r1-r0.…...

[计算机入门] 应用软件(办公类)

3.19 应用软件(办公类) 3.19.1 Microsoft office办公软件套件 Microsoft Office 是一套广泛使用的办公软件套件&#xff0c;由Microsoft公司开发和发布。它包含了多个应用程序&#xff0c;用于处理各种办公任务。以下是Office常见的几个应用程序&#xff1a; Microsoft Word…...

基于安卓android微信小程序音乐播放器

运行环境 小程序前端框架&#xff1a;uniapp 小程序运行软件&#xff1a;微信开发者 后端技术:javaSsm(SpringSpringMVCMyBatis)vue.js 后端开发环境:idea/eclipse 数据库:mysql 项目介绍 音乐播放器小程序的设计主要是对系统所要实现的功能进行详细考虑&#xff0c;确定所要…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生&#xff0c;我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要&#xff0c;而您认真负责的教学态度&#xff0c;让课程的每一部分都充满了实用价值。 尤其让我…...

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

Angular微前端架构:Module Federation + ngx-build-plus (Webpack)

以下是一个完整的 Angular 微前端示例&#xff0c;其中使用的是 Module Federation 和 npx-build-plus 实现了主应用&#xff08;Shell&#xff09;与子应用&#xff08;Remote&#xff09;的集成。 &#x1f6e0;️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要&#xff1a; 近期&#xff0c;在使用较新版本的OpenSSH客户端连接老旧SSH服务器时&#xff0c;会遇到 "no matching key exchange method found"​, "n…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

如何应对敏捷转型中的团队阻力

应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中&#xff0c;明确沟通敏捷转型目的尤为关键&#xff0c;团队成员只有清晰理解转型背后的原因和利益&#xff0c;才能降低对变化的…...