从源码Debug深入spring事件机制,基于观察者模式仿写spring事件监听骨架

文章目录

  • 1.测试案例
  • 2.DEBUG源码分析
  • 3. 异步监听
  • 4.ApplicationListener子接口
  • 5. 注解支持
  • 6. 基于观察者模式高仿spring事件监听
    • 6.1 先定义自定义一个事件
    • 6.2 定义两个监听器
    • 6.3 定义一个持有所有监听器的对象,类似spring的`SimpleApplicationEventMulticaster`
    • 6.4 事件发布测试

1.测试案例

定义一个事件

package com.example.demo.event;
import org.springframework.context.ApplicationEvent;public class MyEvent extends ApplicationEvent {public MyEvent(Object source) {super(source);}
}

定义两个listener

package com.example.demo.event;import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;@Component
public class Listener1 implements ApplicationListener<MyEvent> {@Overridepublic void onApplicationEvent(MyEvent event) {String source = (String) event.getSource();System.out.println(this.getClass().getName() + ":" + source);}
}
package com.example.demo.event;import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;@Component
public class Listener2 implements ApplicationListener<MyEvent> {@Overridepublic void onApplicationEvent(MyEvent event) {String source = (String) event.getSource();System.out.println(this.getClass().getName() + ":" + source);}
}

注入spring容器里的ApplicationEventPublisher对象,发布事件

package com.example.demo;import com.example.demo.event.MyEvent;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.test.context.junit4.SpringRunner;import javax.annotation.Resource;@RunWith(SpringRunner.class)
@SpringBootTest
public class EventPublisherTest {@Resourceprivate ApplicationEventPublisher eventPublisher;@Testpublic void test() {eventPublisher.publishEvent(new MyEvent("xxx"));}
}

2.DEBUG源码分析

eventPublisher.publishEvent(new MyEvent("xxx"));进去很容易就能找到,可以发现SimpleApplicationEventMulticaster这个事件发布对象持有所有listenter对象及MyEvent对象,
事件发布过程其实就是遍历拿到每个listener对象并调用它自己的onApplicationEvent()方法

SimpleApplicationEventMulticaster类的主要方法:

  • addApplicationListener(ApplicationListener<?> listener) :
  • addApplicationListenerBean(String listenerBeanName):
  • removeApplicationListener(ApplicationListener<?> listener):
  • removeApplicationListenerBean(String listenerBeanName):
  • multicastEvent(ApplicationEvent event):广播事件;
  • multicastEvent(ApplicationEvent event, @Nullable
    ResolvableType eventType):广播事件,指定事件的source类型。

在这里插入图片描述

3. 异步监听

从上图断点可以看到ApplicationEventMulticaster对象持有有taskExecutor字段为null,导致没有异步执行所有监听器。这里需要想办法这个字段设置线程池即可:
springboot默认会配置一个ThreadPoolTaskExecutor对象在容器里,这里把它拿出来设置给ApplicationEventMulticaster对象即可

package com.example.demo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import javax.annotation.PostConstruct;
import javax.annotation.Resource;/*** @author Administrator*/
@SpringBootApplication
public class DemoApplication {@Resourceprivate SimpleApplicationEventMulticaster eventMulticaster;@Resourceprivate ThreadPoolTaskExecutor executor;@PostConstructpublic void setEventExecutor() {eventMulticaster.setTaskExecutor(executor);}public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}

4.ApplicationListener子接口

还可以实现子类的getOrder方法可以实现多个监听器排序;实现supportsEventType,supportsSourceType可以实现按过滤Class过来或按事件源Source的Class过滤
在这里插入图片描述

5. 注解支持

这里不通过实现接口来实现监听,顺序排序,按事件类型过滤监听,采用更方便的注解实现,且一个类中就可以实现多个监听

package com.example.demo.event;import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;@Component
public class Listener3 {@Order(Integer.MIN_VALUE) // 优先级最高@EventListener(MyEvent.class)  // 只监听指定类型的事件public void onApplicationEvent(ApplicationEvent event) {MyEvent myEvent = (MyEvent) event;System.out.println(this.getClass().getName() + ":" + myEvent.getSource());}
}

6. 基于观察者模式高仿spring事件监听

6.1 先定义自定义一个事件

Java中已定义观察者模式事件及监听器顶级接口

package com.example.demo.javanativeevent;import java.util.EventObject;public class MyJavaNativeEvent extends EventObject {public MyJavaNativeEvent(Object source) {super(source);}
}

6.2 定义两个监听器

因为Java提供的EventListener无具体监听方法,且无合适的子接口,故这里自定义一个类似spirng的子接口NativeEventListener。这样后续事件发布就是遍历这种类型接口并调用onApplicationEvent()方法

package com.example.demo.javanativeevent;import java.util.EventListener;
import java.util.EventObject;public interface NativeEventListener extends EventListener {void onApplicationEvent(EventObject event);
}
package com.example.demo.javanativeevent;import java.util.EventObject;public class JavaNativeListener1 implements NativeEventListener {@Overridepublic void onApplicationEvent(EventObject event){String source = (String)event.getSource();System.out.println(this.getClass().getName()+":"+source);}
}

6.3 定义一个持有所有监听器的对象,类似spring的SimpleApplicationEventMulticaster

package com.example.demo.javanativeevent;import java.util.ArrayList;
import java.util.Collections;
import java.util.EventObject;
import java.util.List;/*** TODO** @author majun* @version 1.0* @since 2023-08-13 18:50*/
public class SimpleJavaNativeEventMulticaster {public List<NativeEventListener> eventListeners= Collections.synchronizedList(new ArrayList<>(8));public void addListener(NativeEventListener listener){this.eventListeners.add(listener);}public void multicastEvent(EventObject eventObject){eventListeners.stream().parallel().forEach(listener -> {listener.onApplicationEvent(eventObject);});}
}

6.4 事件发布测试

package com.example.demo.javanativeevent;/*** TODO** @author majun* @version 1.0* @since 2023-08-13 19:01*/
public class EventPublishTest {public static void main(String[] args) {// 注册两个listener,这是spirng把这个过程隐藏在spring容器初始化过程中了SimpleJavaNativeEventMulticaster eventMulticaster = new SimpleJavaNativeEventMulticaster();eventMulticaster.addListener(new JavaNativeListener1());eventMulticaster.addListener(new JavaNativeListener2());// 发布事件eventMulticaster.multicastEvent(new MyJavaNativeEvent("yyyy"));}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么是响应式设计?列举几种实现响应式设计的方法。

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是响应式设计&#xff1f;⭐ 实现响应式设计的方法⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏…

Python实现图片文本支持中文,自定义字体

Python实现图片文本支持中文&#xff0c;自定义字体 # 支持中文 import matplotlib #用下载好的字体文件设置字体&#xff0c;从而正确显示中文 myfont matplotlib.font_manager.FontProperties(fnamer"./simsun.ttc") # 自定义的字体文件 plt.figure(figsize (1…

STM32F429IGT6使用CubeMX配置外部中断按键

1、硬件电路 2、设置RCC&#xff0c;选择高速外部时钟HSE,时钟设置为180MHz 3、配置GPIO引脚 4、NVIC配置 PC13相同 5、生成工程配置 6、部分代码 中断回调函数 /* USER CODE BEGIN 0 */void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {if(GPIO_Pin GPIO_PIN_0){HAL_GPIO…

化工行业案例 | 甄知科技助力万华化学重构IT服务价值,打造信息中心ERP!

随着科技的发展&#xff0c;新材料的应用领域与日俱增&#xff0c;近年来&#xff0c;全球化工新材料产业发展整体步入高技术引领、产品迭代速度快、产业规模和需求不断扩大的阶段。一体化协同与数字化转型策略是实现化工新材料生产原料自给、节能降耗、降低排放和物料成本的重…

QT之UDP通信

QT之UDP通信 UDP不分客户端口服务器,只需要使用一个类QUdpSocket QT += core gui networkgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsTARGET = udp TEMPLATE = app# The following define makes your compiler emit warnings if you use # any feature of Qt …

编译iOS系统可用的FFmpeg

在进行编译之前&#xff0c;需要做一些准备工作安装必备文件&#xff1a; 1 安装 gas-preprocessor FFmpeg-iOS-build-script 自动编译脚本需要使用到 gas-preprocessor . 执行 sudo git clone https://github.com/bigsen/gas-preprocessor.git /usr/local/bin/gas sudo c…

计算机网络-专业术语

计算机网络-专业术语 实体 实体:任何可发送或接收信息的硬件或软件进程 对等实体:收发双方相同层次中的实体 协议 控制两个对等实体进行逻辑通信的规则的集合 协议三要素 语法 定义所交换的信息的格式 是用户数据与控制信息的结构和格式 语义 定义收发双方所需要完成的操作…

HTML表单标签大全并附有详细代码+案例

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大二在校生 &#x1f43b;‍❄️个人主页&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;落798. &#x1f54a;️系列专栏&#xff1a;零基础学java ----- 重识c语言 ---- 计算机网络—【Spring技术内幕】…

UE5.2 LyraDemo源码阅读笔记(四)

上一篇&#xff08;三&#xff09;讲到在模式玩法UI点击Elimination进入淘汰赛模式。 UI选择点击Elimination后&#xff0c;触发蓝图W_HostSessionScreen的HostSession节点&#xff0c;有&#xff1a; 调用这个方法切换关卡后&#xff0c;会调用到LyraGameMode.cpp的 ALyraGam…

【ES】笔记-函数参数默认值

函数参数默认值 ES6 允许给函数参数赋值初始值 1. 形参初始值 具有默认值的参数&#xff0c;一般放到最后 function add(a,b,c10){return abc}let resultadd(1,2);console.log(result);2. 与解构赋值结合 function connect({host"127.0.0.1",username,password,port…

【rust/egui】(一)从编译运行template开始

说在前面 rust新手&#xff0c;egui没啥找到啥教程&#xff0c;这里自己记录下学习过程环境&#xff1a;windows11 22H2rust版本&#xff1a;rustc 1.71.1egui版本&#xff1a;0.22.0eframe版本&#xff1a;0.22.0rust windows安装参考&#xff1a;这里本文默认读者已安装相关环…

微服务学习笔记-基本概念

微服务是一种经过良好架构设计的分布式架构方案。根据业务功能对系统做拆分&#xff0c;每个业务功能模块作为独立项目开发&#xff0c;称为一个服务。 微服务的架构特征&#xff1a; 单一职责&#xff1a;微服务拆分粒度更小&#xff0c;每一个服务都对应唯一的业务能力&…

react 生命周期方法

组件的生命周期 每个组件都包含 “生命周期方法”&#xff0c;你可以重写这些方法&#xff0c;以便于在运行过程中特定的阶段执行这些方法。你可以使用此生命周期图谱作为速查表。在下述列表中&#xff0c;常用的生命周期方法会被加粗。其余生命周期函数的使用则相对罕见。 挂…

github版面混乱加载不出的解决办法

最近出现打开github 界面加载不成功&#xff0c;网页访问乱码&#xff0c;打开chrome的检查发现 github的github.githubassets.com 拒绝访问&#xff0c; 解法&#xff1a; 1.先打开hosts文件所在的目录C:\Windows\System32\drivers\etc 2.右键点击hosts文件-选择用记事本或者…

【LVS-NAT配置】

配置 node1&#xff1a;128&#xff08;客户端&#xff09; node2&#xff1a;135&#xff08;调度器&#xff09; RS&#xff1a; node3&#xff1a;130 node4&#xff1a;132 node2添加网络适配器&#xff08;仅主机模式&#xff09; [rootnode2 ~]# nmtui[rootnode2 ~]#…

软件测试基础篇——Linux

1、Linux系统的特征 开源免费&#xff1a; 开源&#xff1a;开放源代码&#xff0c;指的是底层的源代码是可以开放出来&#xff0c;给相关的开发者&#xff0c;根据实际的需求做出修改的。 免费&#xff1a;不花钱&#xff0c;自由传播。 ​ Linux是一种免费使用和自由传播的…

设计模式(2)工厂方法模式

一、 1、介绍&#xff1a;定义一个用于创建对象的接口&#xff0c;让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断&#xff0c;根据客户端的选择条件动态实例化相关的类&#xff0c;对于客户端来说…

Docker启动、停止、删除容器的相关指令

关闭容器指令&#xff1a; docker stop name启动命令&#xff1a; docker start name删除容器&#xff1a; docker rm name 或 id查看所有容器id&#xff1a; docker ps -aq删除所有容器&#xff1a; docker rm docker ps -aq开启着的容器是不能被删除的。 查看容器信息&…

【LangChain学习】基于PDF文档构建问答知识库(二)创建项目

这里我们使用到 fastapi 作为项目的web框架&#xff0c;它是一个快速&#xff08;高性能&#xff09;的 web 框架&#xff0c;上手简单。 一.创建 FastAPI 项目 我们在IDE中&#xff0c;左侧选择 FastAPI &#xff0c;右侧选择创建一个新的虚拟环境。 创建成功&#xff0c;会有…

Java 的 Stream

一、创建 Stream 1.1、创建 Stream 流 1.1.1、List 集合获取 Stream 流 Collection<String> list new ArrayList<>(); Stream<String> s1 list.stream(); 1.1.2、Map 集合获取 stream 流 Map<String, Integer> map new HashMap<>(); // …