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

2023-10-08 15:44

本文主要是介绍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下集(九)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Python开发文字版随机事件游戏的项目实例

《Python开发文字版随机事件游戏的项目实例》随机事件游戏是一种通过生成不可预测的事件来增强游戏体验的类型,在这篇博文中,我们将使用Python开发一款文字版随机事件游戏,通过这个项目,读者不仅能够... 目录项目概述2.1 游戏概念2.2 游戏特色2.3 目标玩家群体技术选择与环境准备3.1 开发环境3

Java 实用工具类Spring 的 AnnotationUtils详解

《Java实用工具类Spring的AnnotationUtils详解》Spring框架提供了一个强大的注解工具类org.springframework.core.annotation.Annot... 目录前言一、AnnotationUtils 的常用方法二、常见应用场景三、与 JDK 原生注解 API 的

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll

Spring Security自定义身份认证的实现方法

《SpringSecurity自定义身份认证的实现方法》:本文主要介绍SpringSecurity自定义身份认证的实现方法,下面对SpringSecurity的这三种自定义身份认证进行详细讲解,... 目录1.内存身份认证(1)创建配置类(2)验证内存身份认证2.JDBC身份认证(1)数据准备 (2)配置依

SpringBoot整合OpenFeign的完整指南

《SpringBoot整合OpenFeign的完整指南》OpenFeign是由Netflix开发的一个声明式Web服务客户端,它使得编写HTTP客户端变得更加简单,本文为大家介绍了SpringBoot... 目录什么是OpenFeign环境准备创建 Spring Boot 项目添加依赖启用 OpenFeig

Java Spring 中 @PostConstruct 注解使用原理及常见场景

《JavaSpring中@PostConstruct注解使用原理及常见场景》在JavaSpring中,@PostConstruct注解是一个非常实用的功能,它允许开发者在Spring容器完全初... 目录一、@PostConstruct 注解概述二、@PostConstruct 注解的基本使用2.1 基本代