【问题标题】:CDI 2.0: Observing multiple events in one and the same observer instanceCDI 2.0:在同一个观察者实例中观察多个事件
【发布时间】:2016-11-05 13:59:23
【问题描述】:

我有两个触发两个事件的类(代码来自带有私有内部类的 junit 测试):

private static class EventEmitter1 { @Inject private Event<EventData1> event; }
private static class EventEmitter2 { @Inject private Event<EventData2> event; }

在我的测试中 EventData 类是微不足道的:

private class EventData1 { }
private class EventData2 { }

在接收器类中,我想等到两个事件都收到,所以我尝试了这样的事情:

private static class EventReceiver {
    private boolean eventData1Received = false;
    private boolean eventData2Received = false;

    private void receiveEventData1(@Observes EventData1 eventData) {
        LOGGER.debug("received " + eventData.getClass().getName());
        eventData1Received = true;
        handleReceivedEvents();
    }

    private void receiveEventData2(@Observes EventData2 eventData) {
        LOGGER.debug("received " + eventData.getClass().getName());
        eventData2Received = true;
        handleReceivedEvents();
    }

    private void handleReceivedEvents() {
        LOGGER.debug("eventData1Received: " + eventData1Received + ", eventData2Received: " + eventData2Received); 
    }
}

在我的测试方法中,我手动为两个发射器类选择一个实例,并为每个实例触发一个事件。此外,我手动选择了一个 Receiver 实例。

@Test public void receiveEvents() {
    EventReceiver eventReceiver = CDI.current().select(EventReceiver.class).get();

    Instance<EventEmitter1> instanceEventEmitter1 = CDI.current().select(EventEmitter1.class);
    EventEmitter1 eventEmitter1 = instanceEventEmitter1.get();
    EventData1 eventData1 = new EventData1();
    eventEmitter1.event.fire(eventData1);

    Instance<EventEmitter2> instanceEventEmitter2 = CDI.current().select(EventEmitter2.class);
    EventEmitter2 eventEmitter2 = instanceEventEmitter2.get();
    EventData2 eventData2 = new EventData2();
    eventEmitter2.event.fire(eventData2);
}

通过 CDI.current.select(...) 手动选择应该确保 CDI 机制正常工作。

我现在的问题是这种方式三! EventReceiver 实例被初始化。他们都没有将 eventDataXReceived 字段设置为 true。将事件传递到同一个 EventReceiver 实例的正确方法是什么?理想情况下,EventReceiver 不应是(应用程序范围的)单例。

感谢您的帮助。

【问题讨论】:

    标签: java events cdi observers


    【解决方案1】:

    恐怕您需要为您的EventReceiver 添加一个范围,而@ApplicationScoped 通常是这里的首选。

    您可以添加一个辅助逻辑层,这将帮助您确定此事件的来源,从而确定您是否已经注册了两个需要的事件(我想这就是您所追求的)。

    一种可能的方法是将此类信息存储在事件负载中。然后,一旦您观察到事件,提取此信息并保留已注册事件的集合。添加到此集合后,您可能会检查是否已经有两个给定“类型”的事件。

    我希望您了解基本概念,更准确地说,我需要了解更多有关代码的信息。

    最后但并非最不重要的一点是,define your own scope 还有一个(疯狂的)选项,您可以根据自己的需要进行定制。然而,这需要一些认真的投入和工作。

    【讨论】:

      【解决方案2】:

      给 EventReceiver 一个合适的范围。没有范围,它是@Dependend,因此对于每个事件都会创建一个新实例。

      @ApplicationScoped 应该可以正常工作。

      【讨论】:

      • 感谢您的即时答复。是的,@ApplicationScoped 可以解决问题,但我不希望 EventReceiver 成为单例。我编辑了这个问题,因为我没有在原始帖子中说清楚。是否有另一种方法可以将多个事件传递到单个接收器实例?目标环境是 JSE,而不是 JEE。
      • 这种方法有一些变体,例如将事件转发到单例,或使用静态字段,或不是单例的自定义范围。但基本上都是一样的。 @ApplicationScope 有什么问题?你想解决什么问题?
      • 转发事件听起来很有趣,很快就会尝试。我想以松散耦合的方式绑定 UI 组件属性(实际上是 JavaFX)。控制器等待组件可用,然后绑定组件属性。在我的情况下,组件可能会或可能不会变得可用......
      • 我不明白您对使用应用范围的担忧。
      • Afaik 应用范围使带注释的类型成为单例。就我而言,我想在多个地方使用 UI 组件。想像一个 TableView 在左侧显示表 A,表 B 在表 A 旁边,依此类推。对于每个 TableView,我需要一个单独的 TableViewController,所以 TableViewController 不应该是单例的。
      【解决方案3】:

      这是一个适合我的解决方案:

      1. 创建一个抽象事件分派器类型,用于观察您感兴趣的通用事件类型 E。通用方法可能如下所示:
      public abstract class EventDispatcher<E>
      {
          private Set<Consumer<E>> consumers = new HashSet<>();
      
          public Set<Consumer<E>> getConsumers() { return consumers; }
      
          protected void observe(@Observes E event)
          {
              consumers.forEach(consumer -> consumer.accept(event));
          }
      }
      

      注意:EventDispatcher 提供getConsumers() 允许注册对E感兴趣的消费者。

      1. 为自定义事件 E(以下示例中的 Event)创建一个非抽象 EventDispatcher,并为其指定范围(以下示例中为 Singleton):
          @Singleton private static class Dispatcher extends EventDispatcher<Event> { }
      
      

      注意:private static 类型完全可以满足它的小任务。

      1. 在 CDI 应用程序的任何位置使用 (@Inject) Dispatcher
          @Inject private Dispatcher dispatcher;
      
      1. 将消费者添加到您的dispatcher 并在Event 出现时做您认为合适的事情,例如onEvent(event):
              dispatcher.getConsumers().add(event -> onEvent(event));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-02-21
        • 2013-12-03
        • 2021-04-13
        • 2018-06-11
        相关资源
        最近更新 更多