• Guava的EventBus


    Guava的EventBus

    一、EventBus介绍和简单实用

    EventBus允许发布订阅模式中组建不用明确的注册,就可以相互进行沟通。它专为使用显式注册替换传统的 Java 进程内事件分发而设计。它不是一个通用的发布-订阅系统,也不是用于进程间通信的。

    示例:

    // 监听者
    public class EventBusChangeRecord {
        /**
         * 订阅消息
         * @param changeEvent
         */
        @Subscribe
        public void recordCustomerChange(ChangeEvent changeEvent) {
            System.out.println("Listener: " + changeEvent.getData());
        }
    }
    
    // 第二个监听者
    public class EventListener02 {
    
        @Subscribe
        public void run(ChangeEvent changeEvent) {
            System.out.println("run: "+changeEvent.getData());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    // 主题
    public class ChangeEvent {
        private String data;
    
        public void changeData(String data) {
            this.data = data;
        }
    
        public String getData() {
            return data;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    public class EventBusDemo {
    
        public static void main(String[] args) {
            EventBus eventBus = new EventBus();
            // 注册监听者
            eventBus.register(new EventBusChangeRecord());
            // 注册第二个监听者
            eventBus.register(new EventListener02());
    
            // 下面的代码必须在是最后调用 post()  方法
            ChangeEvent changeEvent = new ChangeEvent();
            // 改变主题的属性
            changeEvent.changeData("hello, EventBus");
            // 发布主题
            eventBus.post(changeEvent);
            // 改变主题的属性
            changeEvent.changeData("hello, EventBus2");
            eventBus.post(changeEvent);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    -- 运行上面代码将会输出
    Listener: hello, EventBus
    run: hello, EventBus
    Listener: hello, EventBus2
    run: hello, EventBus2
    
    • 1
    • 2
    • 3
    • 4
    • 5

    二、将基于EventListener的系统修改为基于EventBus

    2.1 对于监听者:Listener

    (1)为了监听到特殊类型的事件(例如:CustomerChangeEvent):

    • 在传统的Java事件:实现一个定义了带有事件的接口。例如CustomerChangeEventListener;
    • 使用EventBus:创建一个方法它接收CustomerChangeEvent作为唯一的参数,并在上面用@Subscribe注解修饰;

    (2) 向事件生产者注册你的监听器方法

    • 在传统的Java事件:传递你的对象到每个生产者的registerCustomerChangeEventListener方法中。这些方法很少被定义在公共的接口中,因此为了知道每个可能的生产者,你必须知道它们的类型;
    • 使用EventBus:传递你的对象到EventBus的EventBus.register(Object) 方法中。你需要确保你的对象和生产者共享一个EventBus。

    (3) 监听公共事件超级类型

    • 在传统的Java事件:不容易
    • 使用EventBus:事件会自动分派给任何超类型的监听器。允许接口类型的监听器或Object的“通配符监听器”。

    (4) 监听和探测在没有监听器情况下分派的事件

    • 在传统的Java事件:添加代码到每个事件分发方法(或者使用AOP)
    • 使用EventBus:订阅DeadEvent。EventBus将通知您任何已发布但未交付的事件。

    2.2 对于生产者:Producers

    (1)为了追踪事件的听众:

    • 在传统的Java事件:写代码管理一个监听器的列表到你的对象中。包含同步,或者使用工具类,像EventListenerList
    • 使用EventBus:EventBus会为你做这些事情。

    (2)发布一个事件给监听着

    • 在传统的Java事件:写一个方法发布事件到每一个监听者,包括错误隔离和同步。
    • 使用EventBus:将事件对象传递给EventBus的EventBus.post(Object)方法。

    三、词汇表

    EventBus系统和代码使用下面的术语讨论事件分发:

    事件能够传递到bus到对象
    订阅(Subscribing)向EventBus注册监听器行为,以便它的处理程序接收事件。
    监听器(Listener)一个希望接收事件的对象,通过暴露处理方法。
    处理方法(Handler method)一个公开的方法,EventBus应该用来发布事件。处理程序方法由@Subscribe注解标注
    传递一个事件(Posting an event)通过EventBus让事件对任何的监听器管用。

    四、常见问题

    4.1 为什么必须创建一个自己的EventBus,而不是使用一个单例的

    EventBus没有指定如何使用它。没有什么可以阻止你的应用程序为每个组件拥有单独的EventBus实例,或者使用单独的实例来按照上下文或主题来分隔事件。这也使得在测试中设置和拆除 EventBus 对象变得很简单。

    当然,如果你想要一个进程范围的 EventBus 单例,没有什么能阻止你这样做。只需让您的容器在全局范围内将 EventBus 创建为单例(或将其存储在静态字段中,如果您喜欢这种情况)。

    简而言之,EventBus 不是一个单例,因为我们宁愿不为您做出那个决定。 随心所欲地使用它。

    4.2 能否从一个EventBus中注销一个监听者

    可以的,使用EventBus.unregister(Object) 方法,但我们发现这很少需要:

    • 大多数侦听器在启动或延迟初始化时注册,并在应用程序的生命周期内持续存在。
    • 范围特定的 EventBus 实例可以处理临时事件分发(例如,在请求范围的对象之间分发事件)
    • 对于测试,EventBus 实例可以轻松创建和丢弃,无需显式注销。

    4.3 为什么使用注解标注处理方法,而不是要求监听器实现接口?

    我们认为 Event Bus 的 @Subscribe 注释与实现接口一样明确地传达了您的意图(或者可能更明确),同时让您可以自由地将事件处理程序方法放置在您希望的任何位置,并为它们提供意图揭示的名称。

    传统的 Java 事件使用侦听器接口,该接口通常只支持少数几种方法——通常是一个。 这有许多缺点:

    • 任何一个类都只能实现对给定事件的单个响应。
    • 侦听器接口方法可能会发生冲突。
    • 该方法必须以事件命名(例如,handleChangeEvent),而不是其目的(例如,recordChangeInJournal)。
    • 每个事件通常都有自己的接口,没有用于一系列事件(例如所有 UI 事件)的公共父接口。
  • 相关阅读:
    python绘图技巧(高清图)
    Python文件读写实战:处理日常任务的终极工具!
    【WALT】调度与负载计算(未更新完)
    CV:基于计算机视觉完成两张图片的特征匹配以及用RANSAC方法寻找最佳的匹配点对和单应矩阵的代码
    Jenkins
    单商户商城系统功能拆解03—店铺主页编辑
    互斥锁、条件变量、信号量以及适用场景
    记录一个在公司写的简单Demo使用串口通信
    实测 ubuntu 20.04 编译LIO-SAM问题与解决办法
    【华为OD机试python】斗地主之顺子【2023 B卷|100分】
  • 原文地址:https://blog.csdn.net/hefrankeleyn/article/details/126202409