如何自己实现事件的订阅和发布呢?

1.原理

核心思想是基于发布/订阅模式,用一个共享的数据结构来管理事件和事件监听器。主要功能包括事件订阅、取消订阅、发布事件等功能。

实现思路

  1. 定义事件和监听器接口:首先定义一个 Event 类和一个 EventListener 接口,所有事件和监听器都继承自它们。
  2. 管理订阅关系:使用 Map<Class<? extends Event>, List<EventListener<?>>> 来存储事件类型与监听器的关系。
  3. 发布事件:通过指定事件类型查找所有订阅的监听器,并调用监听器的处理方法。

2.代码工程

Step 1: 定义基础的事件和监听器接口

package com.et;// base event
public abstract class Event {// some basic filed
}
package com.et;// event listen interface
public interface EventListener<T extends Event> {void onEvent(T event);
}

Step 2: 实现 EventBus 类

package com.et;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class EventBus {// store eventprivate final Map<Class<? extends Event>, List<EventListener<? extends Event>>> listeners = new HashMap<>();// Subscribepublic <T extends Event> void register(Class<T> eventType, EventListener<T> listener) {listeners.computeIfAbsent(eventType, k -> new ArrayList<>()).add(listener);}// cancel Subscribepublic <T extends Event> void unregister(Class<T> eventType, EventListener<T> listener) {List<EventListener<? extends Event>> eventListeners = listeners.get(eventType);if (eventListeners != null) {eventListeners.remove(listener);}}// publish@SuppressWarnings("unchecked")public <T extends Event> void post(T event) {List<EventListener<? extends Event>> eventListeners = listeners.get(event.getClass());if (eventListeners != null) {for (EventListener<? extends Event> listener : eventListeners) {((EventListener<T>) listener).onEvent(event); // event forward}}}
}

Step 3: 定义自定义事件和监听器

package com.et;// UserLoginEvent
public class UserLoginEvent extends Event {private final String username;public UserLoginEvent(String username) {this.username = username;}public String getUsername() {return username;}
}

package com.et;public class UserLoginListener implements EventListener<UserLoginEvent> {@Overridepublic void onEvent(UserLoginEvent event) {System.out.println("User logged in: " + event.getUsername());}
}

代码解释

  1. register 方法:将监听器与事件类型绑定。
  2. post 方法:发布事件,找到所有订阅该事件的监听器并通知它们。
  3. unregister 方法:从事件类型的监听器列表中移除指定监听器。

优化建议

  1. 异步处理:可以使用线程池异步发布事件,提高性能。
  2. 过滤器机制:支持对事件的过滤,以便监听器只接收满足条件的事件。
  3. 事件优先级:可以为监听器定义优先级,根据优先级顺序处理。

这种简单的 EventBus 实现适合单进程内的轻量事件传递。如果需要更复杂的功能(如分布式支持、持久化消息),则可以使用成熟的消息代理系统,如 RabbitMQ 或 Kafka。

以上只是一些关键代码,所有代码请参见下面代码仓库

代码仓库

  • GitHub - Harries/Java-demo(customeMq)

3.测试

package com.et;public class EventBusTest {public static void main(String[] args) {EventBus eventBus = new EventBus();// registerUserLoginListener userLoginListener = new UserLoginListener();eventBus.register(UserLoginEvent.class, userLoginListener);// publishUserLoginEvent loginEvent = new UserLoginEvent("Alice");eventBus.post(loginEvent);// unregistereventBus.unregister(UserLoginEvent.class, userLoginListener);eventBus.post(new UserLoginEvent("Bob")); // don't take effort,because the event is unregister}
}

运行代码,返回结果

User logged in: Alice

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

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

相关文章

【CSS】“flex: 1“有什么用?

flex 属性的组成 flex 属性是一个复合属性&#xff0c;包含以下三个子属性&#xff1a; flex-grow&#xff1a;决定元素在容器中剩余空间的分配比例。默认值为 0&#xff0c;表示元素不会扩展。当设置为正数时&#xff0c;元素会按照设定比例扩展。flex-shrink&#xff1a;决…

计算机课程管理:Spring Boot与工程认证的协同创新

3系统分析 3.1可行性分析 通过对本基于工程教育认证的计算机课程管理平台实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本基于工程教育认证的计算机课程管理平…

vuepress配置谷歌广告-通过vue-google-adsense库

在 VuePress 中可以使用 vue-google-adsense 插件来配置 Google AdSense 广告&#xff0c;这个库可以简化在 VuePress 项目中插入广告的过程。 以下是使用 vue-google-adsense 配置广告的步骤&#xff1a; 1. 安装 vue-google-adsense 库 在你的 VuePress 项目根目录下安装 …

深度学习:解码器如何与编码器交互的过程

解码器如何与编码器交互的过程 在序列到序列的神经网络模型中&#xff0c;解码器与编码器的交互是实现有效翻译、文本生成等任务的关键环节。这种交互主要是通过编码器-解码器注意力机制&#xff08;通常称为跨注意力机制&#xff09;来实现的&#xff0c;它允许解码器在生成每…

通过VirtualBox虚拟机安装和调试编译好的 ReactOS

1. 首先创建一个虚拟机配置脚本 setup_vm.bat&#xff1a; batch echo off :: setup_vm.bat :: 设置路径 set "REACTOS_BUILDE:\Reactos_WinDriver\reactos-master\build" set "VM_PATHE:\VMs\ReactOS_Debug" set "VBOX_MANAGEC:\Program Files\Ora…

Kubernetes-Kubectl篇-01-常用命令

kubectl 常见命令 登录命令 根据机器ip使用kubectl登录机器(field-selector): #!/bin/bash export targetIp"6.0.90.240"#alias kubectlkubectl alias kubectlkubectl --kubeconfig/Users/king/.kube/sa128.configpodinfokubectl get pod --all-namespaces --fiel…

【SpringBoot】18 上传文件到数据库(Thymeleaf + MySQL)

Git仓库 https://gitee.com/Lin_DH/system 介绍 使用 Thymeleaf 写的页面&#xff0c;将&#xff08;txt、jpg、png&#xff09;格式文件上传到 MySQL 数据库中。 依赖 pom.xml <!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j --><depende…

Kubernetes实现故障转移和微服务弹性伸缩

以下是在Kubernetes&#xff08;k8s&#xff09;中实现故障转移和高可用的方法及详细操作步骤&#xff1a; 一、通过Kubernetes Deployment实现故障转移&#xff08;多实例部署&#xff09; 方法概述&#xff1a; 通过创建Deployment资源并设置多个副本&#xff0c;Kubernete…

Sharding运行模式、元数据、持久化详解

运行模式 单机模式 能够将数据源和规则等元数据信息持久化&#xff0c;但无法将元数据同步至多个Sharding实例&#xff0c;无法在集群环境中相互感知。 通过某一实例更新元数据之后&#xff0c;会导致其他实例由于获取不到最新的元数据而产生不一致的错误。 适用于工程师在本…

Go语言进阶之Context控制并发

Context Context是Go语言中一个用于传递请求范围的上下文信息的标准库包&#xff0c;其主要用于处理并发操作中请求的生命周期的管理。 协程如何退出 利用协程退出的例子来说明Context的作用&#xff0c;以及没有使用Context&#xff0c;应该如何在没有执行完代码时提前退出…

挖掘web程序中的OAuth漏洞:利用redirect_uri和state参数接管账户

本文探讨了攻击者如何利用OAuth漏洞&#xff0c;重点是滥用redirect_uri和state参数以接管用户账户。如果redirect_uri参数验证不严&#xff0c;可能会导致未经授权的重定向到恶意服务器&#xff0c;从而使攻击者能够捕获敏感信息。同样&#xff0c;state参数的错误实现可能使O…

数据中心类DataCenter(二)

数据中心类DataCenter&#xff08;二&#xff09; 前言 在上一集我们对数据中心类DataCenter做了以下内容&#xff0c;我们对他进行设置单例模式&#xff0c;我们讨论并写入了一些我们数据中心类需要管理的数据&#xff0c;重点介绍了我们验证码id的重要性&#xff0c;在最后…

ORACLE 闪回技术简介

闪回技术是若干技术的集合 包含对数据库整体的闪回 对表的闪回 对事务的闪回 经典面试题面试题&#xff1a;简述Oracle数据库闪回技术&#xff1f; 1.闪回Oracle数据库 2.闪回表 3.闪回事务 数据库闪回 要想实现数据库闪回 1.必须配置数据库的恢复区 SQL> show parameter …

Python世界:力扣题解1712,将数组分成三个子数组的方案数,中等

Python世界&#xff1a;力扣题解1712&#xff1a;将数组分成三个子数组的方案数&#xff0c;中等 任务背景思路分析代码实现测试套件本文小结 任务背景 问题来自力扣题目1712. Ways to Split Array Into Three Subarrays&#xff0c;大意如下&#xff1a; A split of an intege…

Java集合框架之映射(Map)

引言 在Java编程中&#xff0c;管理键值对数据是一项常见的任务。Java集合框架中的Map接口为此提供了强大的支持。Map接口允许我们存储键值对&#xff0c;并提供了丰富的方法来操作这些键值对。本文将详细介绍Map接口的内部机制、特性、操作方法以及在实际编程中的应用场景。 …

Java集合基础——针对实习面试

目录 Java集合基础什么是Java集合&#xff1f;说说List,Set,Queue,Map的区别&#xff1f;说说List?说说Set?说说Map&#xff1f;说说Queue?为什么要用集合&#xff1f;如何选用集合&#xff1f; Java集合基础 什么是Java集合&#xff1f; Java集合&#xff08;Java Collect…

基于单片机的客车载客状况自动检测系统(论文+源码)

1系统整体设计 本课题为客车载客状况自动检测系统&#xff0c;在此以STM32单片机为核心控制器&#xff0c;结合压力传感器、红外传感器、蜂鸣器、语音提示模块、继电器、液晶等构成整个客车载客状况自动检测系统&#xff0c;整个系统架构如图2.1所示&#xff0c;在此通过两个红…

卷积核参数详细介绍

卷积核参数详细介绍如下&#xff1a; 2. 尺寸&#xff1a;卷积核通常是一个小矩阵&#xff0c;如3x3、5x5等&#xff0c;定义了卷积的大小范围&#xff0c;在网络中代表感受野的大小。 3. 权重&#xff1a;卷积核中的每个元素都有一个权重值&#xff0c;用于捕捉输入数据的特征…

第 3 章 -GO语言 基本语法

1. 注释 在编程中&#xff0c;注释是帮助理解代码的重要工具。Go语言支持两种类型的注释&#xff1a; 单行注释&#xff1a;以 // 开头&#xff0c;直到行尾都是注释。多行注释&#xff1a;以 /* 开始&#xff0c;以 */ 结束&#xff0c;可以跨越多行。 示例 package maini…

渗透测试(socket,namp,scapy)

socket:可以用来实现不同虚拟机或者不同计算机之间的通信。 socket常用函数&#xff1a; sock.bind(host,port) //host可接受client范围&#xff0c;以及连接的端口 sock.listen()//sever开启监听连接 sock.accpet()//返回 sock&#xff0c;addr 用来接受和发送数据 addr…