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

news2024/11/23 19:29:55

上集从官网的角度讲解了基本的使用和源码的内容,没有深入的进行分析,本章将从源码的角度分析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 {
			// 广播器的创建SimpleApplicationEventMulticaster
			this.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 {

	@Bean
	public ApplicationEventMulticaster applicationEventMulticaster(){
		SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
		return simpleApplicationEventMulticaster;
	}
}

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

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

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

    @Bean
    public 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;

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

	public void sendEmail(String address, String content) {
		applicationEventPublisher.publishEvent(new BlockedListEvent(applicationContext, address, content));
	}

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

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

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

最终会使用监听器调用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
    @EventListener
    public 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)
    @EventListener
    public void firstListener(MyEvent event) {
        // 第一个监听器的逻辑
    }

    @Order(2)
    @EventListener
    public void secondListener(MyEvent event) {
        // 第二个监听器的逻辑
    }
}

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

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

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

总结

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

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

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

相关文章

数据驱动智能护理:看AI如何塑造医疗领域,为灰暗夕阳带来新的曙光

近年来&#xff0c;人工智能的应用正日益渗透到医疗领域&#xff0c;呈现出无限的潜力和前景。技术的不断进步和全球医疗挑战的不断涌现&#xff0c;使得AI成为改善医疗保健质量、提高患者生活水平的强大工具。从疾病的早期诊断到治疗计划的制定&#xff0c;再到医疗管理和患者…

IP 子网划分(VLSM)

目录 一、 为什么要划分子网 二、如何划分子网 1、划分两个子网 2、划分多个子网 一、 为什么要划分子网 假设有一个B类IP地址172.16.0.0&#xff0c;B类IP的默认子网掩码是 255.255.0.0&#xff0c;那么该网段内IP的变化范围为 172.16.0.0 ~ 172.16.255.255&#xff0c;即…

IDEA的database工具以及对比两个数据库之间的差异(比DBVisualizer和DBeaver方便)

背景 其实IDEA里有个非常好用的database工具&#xff0c;用来连数据库&#xff0c;能连的数据非常多&#xff0c;最重要的是还带有对比数据库差异的工具。 唯一有点不好就是这个是 intellij idea的ultimate edition版本才有&#xff0c;对于社区版本&#xff08;community ed…

Tomcat自启动另一种方法

Tomcat自启动另一种方法 问题&#xff1a; 不知道怎么回事&#xff0c;好几台电脑都可以开机自启动tomcat&#xff0c;正常运行项目。一样的配置一样的操作流程&#xff0c;偏偏要运行的机器开机自启动后&#xff0c;项目不能运行&#xff0c;手动重启tomcat又可以用了。网上…

【案例分享】企业常用IPSec VPN实现

【微|信|公|众|号&#xff1a;厦门微思网络】 【微思网络www.xmws.cn&#xff0c;成立于2002年&#xff0c;专业培训21年&#xff0c;思科、华为、红帽、ORACLE、VMware等厂商认证及考试&#xff0c;以及其他认证PMP、CISP、ITIL等】 组网需求 如图1所示&#xff0c;RouterA为…

图像处理与计算机视觉--第五章-图像分割-自适应阈值分割

文章目录 1.自适应阈值分割介绍2.自适应阈值函数参数解析3.高斯概率函数介绍4.自适应阈值分割核心代码5.自适应阈值分割效果展示6.参考文章及致谢 1.自适应阈值分割介绍 在图片处理过程中&#xff0c;针对铺前进行二值化等操作的时候&#xff0c;我们希望能够将图片相应区域内所…

数字IC前端学习笔记:数字乘法器的优化设计(Wallace Tree乘法器)

相关阅读 数字IC前端https://blog.csdn.net/weixin_45791458/category_12173698.html?spm1001.2014.3001.5482 进位保留乘法器依旧保留着阵列的排列规则&#xff0c;只是进位是沿斜下角&#xff0c;如果能使用树形结构来规划这些进位保留加法器&#xff0c;就能获得更短的关键…

ToBeWritten之让响应团队参与并做好沟通

也许每个人出生的时候都以为这世界都是为他一个人而存在的&#xff0c;当他发现自己错的时候&#xff0c;他便开始长大 少走了弯路&#xff0c;也就错过了风景&#xff0c;无论如何&#xff0c;感谢经历 转移发布平台通知&#xff1a;将不再在CSDN博客发布新文章&#xff0c;敬…

网页动画科普LLM原理;淘宝推出AI试衣间;爆火的AI极简人像;100天创业日程表;Llama 2详解 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f525; Meta Connect 2023&#xff0c;发布基于 LLama 2 的AI聊天助手和虚拟角色 9月27日-28日&#xff0c;Meta 举办了年度重要会议 Meta Co…

vue3 + vite项目使用SVG图标

我们在开发一个项目时常会用到一些图标&#xff0c;有些图标在有限的图标库中却是没有&#xff0c;因此少数的图标需要我们去网上查找&#xff0c;或是自定义一些专用的图标。一般很少有图标库满足现实的项目开发&#xff0c;这时将推荐使用 iconify 这个图标库&#xff0c;几乎…

Zabbix 监控系统安装和部署

Zabbix 监控系统安装和部署 一、zabbix 是什么&#xff1f;1.1、zabbix 监控原理&#xff08;重点&#xff09;1.2、Zabbix 6.0 新特性1.3、Zabbix 6.0 功能组件1.4、数据库1.5、Web 界面1.6、Zabbix Agent1.7、Zabbix Proxy1.8、Java Gateway 二、部署Zabbix 6.02.1、 解决 za…

通达信指标预警信号,自动发送给微信好友1.0

1.功能介绍&#xff1a;十一节假日期间写了一个&#xff0c;可将股票指标预警信号&#xff0c;自动发送给微信好友/微信群&#xff08;即电脑端的消息&#xff0c;通过模拟微信操作可在手机上显示&#xff09;。本工具按通达信写的&#xff0c;如果大智慧&#xff0c;同花顺也能…

Flutter环境搭建及新建项目

一、下载安装压缩包 https://storage.flutter-io.cn/flutter_infra_release/releases/stable/windows/flutter_windows_3.10.6-stable.zip 二、解压缩 解压之后&#xff0c;将里面的flutter整体拿出来 三、配置环境变量 将flutter/bin全路径配置到系统环境变量里面 四、运行…

12.2 实现键盘模拟按键

本节将向读者介绍如何使用键盘鼠标操控模拟技术&#xff0c;键盘鼠标操控模拟技术是一种非常实用的技术&#xff0c;可以自动化执行一些重复性的任务&#xff0c;提高工作效率&#xff0c;在Windows系统下&#xff0c;通过使用各种键盘鼠标控制函数实现动态捕捉和模拟特定功能的…

用vscode进行远程主机开发

文章目录 插件操作步骤FQA 插件 Remote - SSH - 通过使用 SSH 打开远程计算机或者VM上的文件夹&#xff0c;来连接到任何位置。 操作步骤 使用Vscode利用Remote进行远端开发必须现在Vscode内安装插件 安装完成后&#xff0c;底部工具栏会出现一个绿色按钮&#xff0c;如下…

Vue中如何进行图像处理与图像滤镜

在Vue中进行图像处理与图像滤镜 图像处理和滤镜效果是现代Web应用程序中常见的功能之一。Vue.js作为一个流行的JavaScript框架&#xff0c;为实现这些功能提供了许多工具和库。本文将介绍如何使用Vue来进行图像处理与图像滤镜&#xff0c;包括使用HTML5 Canvas和CSS滤镜。 准备…

《有了这些可视化应用,再来一场亚运会也不怕!》

亚运会遇上中秋国庆千万大流量涌入城市&#xff0c;旅游、交通、应急消防等部门指挥中心人员&#xff0c;会如何运筹帷幄呢&#xff1f; 还记得刘涛在《开端》里&#xff0c;每次出场都在指挥中心&#xff0c;看着一堆大屏找线索。在早些年《人民的名义》里&#xff0c;汉东省的…

cap分布式理论

cap 理论 cap是实现分布式系统的思想。 由3个元素组成。 Consistency&#xff08;一致性&#xff09; 在任何对等 server 上读取的数据都是最新版&#xff0c;不会读取出旧数据。比如 zookeeper 集群&#xff0c;从任何一台节点读取出来的数据是一致的。 Availability&…

UG\NX二次开发 重命名特征对象 UF_OBJ_set_name

文章作者:里海 来源网站:《里海NX二次开发3000例专栏》 感谢粉丝订阅 感谢 林闹 订阅本专栏,非常感谢。 简介 UG\NX二次开发 重命名特征 UF_OBJ_set_name 效果 代码 #include "me.hpp" #include <vector> #include

RT-Thread 中断管理(学习一)

中断管理 什么是中断&#xff1f;简单的解释就是系统正在处理某一个正常事件&#xff0c;忽然被另一个需要马上处理的紧急事件打断&#xff0c;系统转而处理这个紧急事件&#xff0c;待处理完毕&#xff0c;再恢复运行刚才被打断的事件。生活中&#xff0c;我们经常会遇到这样…