CDI 2.0:在同一个观察者实例中观察多个事件

CDI 2.0: Observing multiple events in one and the same observer instance

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

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

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

private class EventData1 { }
private class EventData2 { }

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

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); 
    }
}

在我的测试方法中,我手动 select 两个发射器 classes 的实例,并为每个实例触发一个事件。此外,我手动 select 一个 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);
}

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

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

感谢您的帮助。

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

@ApplicationScoped 应该可以正常工作。

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

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

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

的两个事件

我希望你能理解基本的想法,更准确地说,我需要更多地了解代码。

最后但同样重要的是,define your own scope 还有一个(疯狂的)选项,您可以根据自己的需要进行调整。然而,这需要认真的投入和努力。

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

  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));