diff --git a/common/src/main/java/cn/funnymap/lgis/eventbus/DemoEvent.java b/common/src/main/java/cn/funnymap/lgis/eventbus/DemoEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..a3d5ba5f212d0bd41cc6cf2c74a20a65a3162792 --- /dev/null +++ b/common/src/main/java/cn/funnymap/lgis/eventbus/DemoEvent.java @@ -0,0 +1,32 @@ +package cn.funnymap.lgis.eventbus; + +/** + * 基于 EventBus 的事件模版 + */ +public class DemoEvent { + + // 事件包含的若干属性 + private final Object source; + private final String pluginId; + private final boolean success; + private final String errorMessage; + + public DemoEvent(Object source, String pluginId, boolean success, String errorMessage) { + this.source = source; + this.pluginId = pluginId; + this.success = success; + this.errorMessage = errorMessage; + } + + public String getPluginId() { + return pluginId; + } + + public boolean isSuccess() { + return success; + } + + public String getErrorMessage() { + return errorMessage; + } +} diff --git a/common/src/main/java/cn/funnymap/lgis/eventbus/DemoListener.java b/common/src/main/java/cn/funnymap/lgis/eventbus/DemoListener.java new file mode 100644 index 0000000000000000000000000000000000000000..0d5a3cc87e401d2a77f8be1fb0cf73f272d303ef --- /dev/null +++ b/common/src/main/java/cn/funnymap/lgis/eventbus/DemoListener.java @@ -0,0 +1,25 @@ +package cn.funnymap.lgis.eventbus; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 监听器模版 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class DemoListener { + + @Subscribe + public void handlePluginExecutionEvent(DemoEvent event) { + try { + log.debug("接收到插件执行事件: pluginId={}, success={}", + event.getPluginId(), event.isSuccess()); + } catch (Exception e) { + log.error("处理插件执行事件失败: pluginId={}, success={}", + event.getPluginId(), event.isSuccess(), e); + } + } +} diff --git a/common/src/main/java/cn/funnymap/lgis/eventbus/EventBus.java b/common/src/main/java/cn/funnymap/lgis/eventbus/EventBus.java new file mode 100644 index 0000000000000000000000000000000000000000..96d276d0e81008fb02074195a9c663a5c07450a4 --- /dev/null +++ b/common/src/main/java/cn/funnymap/lgis/eventbus/EventBus.java @@ -0,0 +1,87 @@ +package cn.funnymap.lgis.eventbus; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; + + +/** + * EventBus 作为事件总线的入口类,提供注册、注销和发布事件的方法 + * + * @author Hanyr + * @since 2025/10/30 14:32 + */ +public class EventBus { + // 事件总线名称,一个名称对应一类事件总线 + private final String name; + // 订阅者集合,存储事件类型与该事件所有的订阅者 + private final Map, Set> subscribers = new ConcurrentHashMap<>(); + + public EventBus() { + this("default-event-bus"); + } + + public EventBus(String name) { + this.name = name; + } + + /** + * 注册事件监听器 + */ + public void register(Object listener) { + if (listener == null) { + throw new IllegalArgumentException("监听者不可为空"); + } + + for (Method method : listener.getClass().getDeclaredMethods()) { + if (method.isAnnotationPresent(Subscribe.class)) { + Class[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length != 1) { + throw new IllegalArgumentException("订阅方法必须有且仅有一个参数"); + } + Class eventType = parameterTypes[0]; + Subscriber subscriber = new Subscriber(listener, method); + subscribers.computeIfAbsent(eventType, k -> new CopyOnWriteArraySet<>()).add(subscriber); + } + } + } + + /** + * 取消注册事件监听器 + */ + public void unregister(Object listener) { + if (listener == null) { + return; + } + + for (Set subscriberSet : subscribers.values()) { + subscriberSet.removeIf(subscriber -> subscriber.getTarget() == listener); + } + } + + /** + * 发布事件 + */ + public void post(Object event) { + if (event == null) { + throw new IllegalArgumentException("事件不能为空"); + } + Class eventType = event.getClass(); + for (Map.Entry, Set> entry : subscribers.entrySet()) { + Class subscriberEventType = entry.getKey(); + // 如果遍历到的事件类型是目标事件的父类或本身,则调用所有订阅者 + if (subscriberEventType.isAssignableFrom(eventType)) { + Set eventSubscriber = entry.getValue(); + for (Subscriber subscriber : eventSubscriber) { + try { + subscriber.execute(event); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } + +} diff --git a/common/src/main/java/cn/funnymap/lgis/eventbus/Subscribe.java b/common/src/main/java/cn/funnymap/lgis/eventbus/Subscribe.java new file mode 100644 index 0000000000000000000000000000000000000000..ceac95a2a1998d357a3b4aa237573e58df280f0f --- /dev/null +++ b/common/src/main/java/cn/funnymap/lgis/eventbus/Subscribe.java @@ -0,0 +1,15 @@ +package cn.funnymap.lgis.eventbus; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 标记方法为事件订阅者 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Subscribe { +} diff --git a/common/src/main/java/cn/funnymap/lgis/eventbus/Subscriber.java b/common/src/main/java/cn/funnymap/lgis/eventbus/Subscriber.java new file mode 100644 index 0000000000000000000000000000000000000000..324d937060fc1c9473bd6adb45a0d46159414e7c --- /dev/null +++ b/common/src/main/java/cn/funnymap/lgis/eventbus/Subscriber.java @@ -0,0 +1,59 @@ +package cn.funnymap.lgis.eventbus; + + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Objects; + +/** + * 事件订阅者封装类 + * + * @author Hanyr + * @since 2025/10/30 16:32 + */ +public class Subscriber { + + private final Object target; + private final Method method; + + public Subscriber(Object target, Method method) { + this.target = Objects.requireNonNull(target); + this.method = Objects.requireNonNull(method); + this.method.setAccessible(true); + } + + /** + * 执行事件处理方法 + */ + public void execute(Object event) throws InvocationTargetException, IllegalAccessException { + method.invoke(target, event); + } + + public Object getTarget() { + return target; + } + + public Method getMethod() { + return method; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Subscriber that = (Subscriber) o; + return Objects.equals(target, that.target) && + Objects.equals(method, that.method); + } + + @Override + public int hashCode() { + return Objects.hash(target, method); + } + + @Override + public String toString() { + return "Subscriber{target=" + target.getClass().getSimpleName() + + ", method=" + method.getName() + '}'; + } +}