Spring事件监听机制使用和原理解析
你好,我是刘牌!
前言
好久没有更新Spring了,今天来分享一下Spring的事件监听机制,之前分享过一篇Spring监听机制的使用,今天从原理上进行解析,Spring的监听机制基于观察者模式,就是就是我们所说的发布订阅模式,这种模式可以在一定程度上实现代码的解耦,如果想要实现系统层面的解耦,那么消息队列就是我们的不二选择,消息队列本身也是发布订阅模式,只是不同的消息队列的实现方式不一样。
使用
之前的文章我们使用了注解的方式,今天我们使用接口的方式来实现。
定义事件
如下定义了一个事件AppEvent,它继承了ApplicationEvent类,如果我们要使用Spring的事件监听机制,那么我们定义的事件必须继承ApplicationEvent ,否则就无法使用。
/**
* 功能说明: 事件
* <p>
* Original @Author: steakliu-刘牌, 2023-03-30 11:02
* <p>
* Copyright (C)2020-2022 steakliu All rights reserved.
*/
public class AppEvent extends ApplicationEvent {
private final String event;
public AppEvent(Object source, String event) {
super(source);
this.event = event;
}
public String getEvent() {
return event;
}
}
定义事件监听器
事件监听器实现了ApplicationLister接口,其泛型为ApplicationEvent,因为要监听事件,所以必须按照Spring的规则来,onApplicationEvent方法就是监听到的事件,在这里我们可以进行我们的业务处理,我们可以看出AppLister我们加上了@Component注解,因为事件监听器需要加入Spring IOC容器中才能生效。
/**
* 功能说明:事件监听器
* <p>
* Original @Author: steakliu-刘牌, 2023-03-30 11:03
* <p>
* Copyright (C)2020-2022 steakliu All rights reserved.
*/
@Component
public class AppListener implements ApplicationListener<AppEvent> {
@Override
public void onApplicationEvent(AppEvent event) {
System.out.println("event: "+event.getEvent());
}
}
事件发布器
有了事件监听器,就需要发布事件,所以就需要一个事件发布器,事件发布器使用的是ApplicationEventPublisher,使用它的publishEvent方法进行事件发布。
/**
* 功能说明:事件发布器
* <p>
* Original @Author: steakliu-刘牌, 2023-06-11 13:55
* <p>
* Copyright (C)2020-2022 steakliu All rights reserved.
*/
@Component
public class AppPublisher {
@Resource
private ApplicationEventPublisher applicationEventPublisher;
public void publish(){
applicationEventPublisher.publishEvent(new AppEvent(new AppListener(),"publish event"));
}
}
测试
为了方便,这里直接使用SpringBoot来进行测试,先获取AppPublisher,然后调用publish发布事件。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
AppPublisher publisher = context.getBean(AppPublisher.class);
publisher.publish();
}
}
上面整个事件发布的代码就写完了,我们可以看出其实还是比较简单的,里面最核心的三个组件分别为,事件(Event)
,监听器(Listener)
,发布器(Publisher)
,实际使用中我们可以根据自己的需求去实现。
原理
上面我们知道了Spring的事件监听机制的基本使用,那么整个事件在Spring中是怎么流转的呢,我们很有必要去弄清楚。
我们使用的是SpringBoot项目来进行测试,我们先找到SpringBoot对事件监听机制进行处理的入口,然后再进行分析,SpringBoot对上下文进行处理的入口类是AbstractApplicationContext,它是Spring的入口,其中我们主要关注的refresh()
方法,因为refresh中的方法比较多,我们下面只保留了三个方法。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Check for listener beans and register them.
registerListeners();
// Last step: publish corresponding event.
finishRefresh();
}
}
}
initApplicationEventMulticaster()
ApplicationEventMulticaster是一个接口,它定义了如何将ApplicationEvent传递给事件监听者(event listener)。该接口有多个实现类,可以使用不同的策略将事件分派给不同的监听者。
ApplicationEventMulticaster为Spring事件机制的核心之一,它支持在应用中传递事件,并且可以将事件广播给多个监听者。在Spring中,事件是由ApplicationEvent及其子类表示的,例如ContextStartedEvent和ContextStoppedEvent等。当某些事件发生时,Spring容器将使用事件广播机制来通知感兴趣的监听者。
这个方法的作用是对ApplicationEventMulticaster进行赋值,Spring在初始化的时候会将ApplicationEventMulticaster注册进IOC容器,这里就只是单纯从IOC容器中获取ApplicationEventMulticaster来进行赋值,以方便后续的使用。
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 {
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() + "]");
}
}
}
registerListeners()
这个方法的作用主要就是注册监听器,它会从IOC容器获取到我们注册的监听器,然后将其加入到Multicaster中,在AbstractApplicationEventMulticaster中,使用一个Set集合来装监听器。
public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
finishRefresh()
finishRefresh()的作用是发布事件,里面是一些发布事件的逻辑,但是由于我们还没有正式发布事件,所以这里并不会发布事件,当我们使用applicationEventPublisher的publishEvent方法发布事件时,才会真正的发布事件。
ApplicationEventPublisher发布事件
上面示例中使用ApplicationEventPublisher的publishEvent发布事件,最终会进入AbstractApplicationContext类中进行事件发布,我们只关注最重要的方法multicastEvent(),它是广播器ApplicationEventMulticaster的一个方法事件都是由广播器进行发布。
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
ApplicationEventMulticaster真正发布事件
ApplicationEventPublisher并没有真正发布事件,它相当于只是抽象了事件的发布,为了让我们更加简单和方便使用,但是真正发布事件的是ApplicationEventMulticaster,在multicastEvent()方法中,如果我们配置了线程池,那么事件就会被加入线程池,从而异步执行,如果没有设置线程池,那么就同步执行,最终执行都是调用invokeListener()方法。
public void multicastEvent(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);
}
}
}
默认是不会使用线程池的,如果我们需要事件异步执行,那么可以配置线程池,其核心就是给广播器SimpleApplicationEventMulticaster的成员变量taskExecutor设置
/**
* 功能说明: 事件任务线程池
* <p>
* Original @Author: steakliu-刘牌, 2023-06-11 13:17
* <p>
* Copyright (C)2020-2022 steakliu All rights reserved.
*/
@Configuration
public class TaskExecutor {
@Bean("eventTaskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(10);
threadPoolTaskExecutor.setMaxPoolSize(20);
threadPoolTaskExecutor.setKeepAliveSeconds(10);
threadPoolTaskExecutor.setThreadNamePrefix("application-event-thread");
threadPoolTaskExecutor.setQueueCapacity(100);
threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
@Bean
public ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
simpleApplicationEventMulticaster.setTaskExecutor(taskExecutor());
return simpleApplicationEventMulticaster;
}
}
invokeListener
invokeListener最终会通过传入的监听器去调用目标监听器,也就是我们自定义的监听器,主要代码如下,我们可以看到最终调用onApplicationEvent方法,就是我们上面示例AppListener监听器的onApplicationEvent方法。
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
listener.onApplicationEvent(event);
}
到这里,整个流程就完了,我们梳理一下重要的组件。
- ApplicationEvent
- ApplicationListener
- ApplicationEventPublisher
- ApplicationEventMulticaster
上面的四个组件基本上就是Spring事件监听机制的全部,ApplicationEvent是事件的规范,ApplicationListener是监听器,ApplicationEventPublisher是发布器,ApplicationEventMulticaster是广播器,其实ApplicationEventMulticaster和ApplicationEventPublisher本质是一样的,都能完成事件的发布,ApplicationEventPublisher最终也是去调用ApplicationEventMulticaster,只不过它只专注于事件发布,单独提出一个接口来,职责更加单一,这也是一种设计思想。
总结
上面对Spring事件监听机制的使用和原理进行了详细的介绍,并对其中涉及的组件进行解析,Spring事件监听机制是一个很不错的功能,我们在进行业务开发的时候可以引入,在相关的开源框架中也是用它的身影,比如高性能网关ShenYu中就使用了Spring事件监听机制来发布网关的更新数据,它可以降低系统的耦合性,使系统的扩展性更好。
今天的分享就到这里,感谢你的观看,我们下期见,如果文中有说得不合理或者不对的地方,希望你能指出,我们进行交流!
Spring事件监听机制使用和原理解析的更多相关文章
- Spring 事件监听机制及原理分析
简介 在JAVA体系中,有支持实现事件监听机制,在Spring 中也专门提供了一套事件机制的接口,方便我们实现.比如我们可以实现当用户注册后,给他发送一封邮件告诉他注册成功的一些信息,比如用户订阅的主 ...
- Java swing(awt):事件监听机制的实现原理+简单示例
(1)实现原理 事件监听机制的实现: 参考图:事件模型_ActionEvent 为了节省资源,系统无法对某个事件进行实时的监听.故实现的机制是当发生某个事件后,处理代码将被自动运行,类似钩子一般.(回 ...
- Spring事件监听机制源码解析
Spring事件监听器使用 1.Spring事件监听体系包括三个组件:事件.事件监听器,事件广播器. 事件:定义事件类型和事件源,需要继承ApplicationEvent. package com.y ...
- Spring事件监听机制
前言 Spring中的事件机制其实就是设计模式中的观察者模式,主要由以下角色构成: 事件 事件监听器(监听并处理事件) 事件发布者(发布事件) 首先看一下监听器和发布者的接口定义 public int ...
- SpringBoot事件监听机制及观察者模式/发布订阅模式
目录 本篇要点 什么是观察者模式? 发布订阅模式是什么? Spring事件监听机制概述 SpringBoot事件监听 定义注册事件 注解方式 @EventListener定义监听器 实现Applica ...
- Spring ApplicationContext(八)事件监听机制
Spring ApplicationContext(八)事件监听机制 本节则重点关注的是 Spring 的事件监听机制,主要是第 8 步:多播器注册:第 10 步:事件注册. public void ...
- Spring的事件监听机制
最近公司在重构广告系统,其中核心的打包功能由广告系统调用,即对apk打包的调用和打包完成之后的回调,需要提供相应的接口给广告系统.因此,为了将apk打包的核心流程和对接广告系统的业务解耦,利用了spr ...
- 7_3.springboot2.x启动配置原理_3.事件监听机制
事件监听机制配置在META-INF/spring.factories ApplicationContextInitializer SpringApplicationRunListenerioc容器中的 ...
- GUI编程笔记(java)05:GUI事件监听机制原理和举例说明
1.事件监听机制: A:事件源 事件发生的地方 B:事件 就是要发生的事情 C:事件处理 就是针对发生的事情做 ...
- java Gui编程 事件监听机制
1. GUI编程引言 以前的学习当中,我们都使用的是命令交互方式: 例如:在DOS命令行中通过javac java命令启动程序. 软件的交互的方式: 1. 命令交互方式 图书管理系统 ...
随机推荐
- 设计模式(二十九)----综合应用-自定义Spring框架-Spring IOC相关接口分析
1 BeanFactory解析 Spring中Bean的创建是典型的工厂模式,这一系列的Bean工厂,即IoC容器,为开发者管理对象之间的依赖关系提供了很多便利和基础服务,在Spring中有许多IoC ...
- nginx+vite 项目打包及部署到服务器二级路由
项目打包及部署到服务器二级路由 例如:我希望将打包的项目部署到 http://localhost:8088/web/ 上 一. 项目配置及打包 项目部署到服务器二级路由需要配置基础路径base,即需要 ...
- Kubernetes(k8s)二进制高可用安装脚本
好久没写公众号了,昨天新写了一个v1.24版本的安装.写得不咋样,但是能用.最近不高产了,没灵感了 = . = 手动部署:https://github.com/cby-chen/Kubernetes ...
- 二进制安装Kubernetes(k8s) v1.24.2 IPv4/IPv6双栈
二进制安装Kubernetes(k8s) v1.24.2 IPv4/IPv6双栈 Kubernetes 开源不易,帮忙点个star,谢谢了 介绍 kubernetes二进制安装 强烈建议在Github ...
- [数据库/Linux]CentOS7安装MySQL Percona版(RPM方式)
OS: CentOS7 (x86_64) MySQL: MySQL Percona 5.7.31-34 0 前置条件 已配置完成YUM源 已卸载先前可能安装的MySQL rpm -qa | grep ...
- Express实现定时发送邮件
在开发中我们有时候需要每隔 一段时间发送一次电子邮件,或者在某个特定的时间进行发送邮件, 无需手动去操作,基于这样的情况下我们需要用到了定时任务,一般可以写个定时器,来完成相应的需求,在 node.j ...
- 基于Containerd容器引擎和kubeadm工具部署K8sv1.26.3
前文我了解了基于ubuntu2204部署containerd容器引擎以及containerd客户端工具的部署和使用相关话题,回顾请参考:https://www.cnblogs.com/qiuhom-1 ...
- JUC(四)多线程锁
目录 多线程锁 Synchronized锁的八种情况 公平锁和非公平锁 可重入锁 synchronized Lock 死锁 检查死锁 多线程锁 Synchronized锁的八种情况 以一个手机类为例, ...
- MySQL(一)Linux下MySQL的安装
Linux下MySQL的安装 1 MySQL的安装 1.1 Linux系统以及工具的准备 这里使用两台CentOS7虚拟机,一台安装8.0版本,另一台克隆的虚拟机安装5.7版本 克隆的虚拟机需要进行配 ...
- Golang网络编程: DNS子域名爆破
域名系统(Domain Name System,缩写:DNS)是互联网的一项服务.它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网.这就如同一个地址簿,根据域名来指向IP地 ...