CDI 事件 fire() 创建新的观察者实例

CDI events fire() create new observers instances

我正在使用 CDI (Weld) 编写 JavaFX 应用程序 (JavaSE)。我正在尝试使用 CDI 事件来替换糟糕的 JavaFX 事件系统(JavaFX 属性)。

我能够让 CDI 正确地创建我的 JavaFX 控制器。这意味着当加载 FXML 时,控制器由 CDI 创建(使用 FXLoader.setControllerFactory)。不幸的是,一旦我在一个控制器中添加了一个@Observes,CDI 就开始为该控制器创建多个实例。太疯狂了。

看来我只是不明白 fire() 的正确语义。从什么时候开始 fire() 会创建观察者?!可能这与 @Dependent 范围有关。

我制作了一个拦截器,当某些 属性 某处发生变化时发送一个事件(在我的例子中,它是一个更新 属性 进度的后台任务)。 (不要关注ObservableSourceLiterral@ObservableSource,只是为了不给大家触发事件)

@ObservableProperty
@Interceptor
public class CDIPropertyWatcher {
    @Inject
    private Event<PropertyChangedEvent> listeners;

    @AroundInvoke
    public Object onPropertyCalled(InvocationContext ctx) throws Exception {
        String method = ctx.getMethod()
                .getName();
        if (method.startsWith("set")) {
            Object result = ctx.proceed();
            String propertyName = method.substring(3, 4)
                    .toLowerCase() + method.substring(4);
            listeners.select(new ObservableSourceLiterral(ctx.getTarget().getClass().getSuperclass()))
                    .fire(new PropertyChangedEvent(ctx.getTarget(), propertyName));

            return result;
        } else {
            return ctx.proceed();
        }
    }
}

这是我任务中的 setter class:

@ObservableProperty
public void setProgress(double progress) {
      this.progress = progress;
}

这是我的控制器负责接收事件的方法:

public void onProgressTaskChanged(@Observes @ObservableSource(Task.class) PropertyChangedEvent evt)
{
        Double progress = evt.getSource(Task.class).getProgress();
        System.out.println("onProgressTaskChanged "+progress+" "+this);
        if (progressBar!=null)
        {
            Platform.runLater(() -> progressBar.setProgress(progress));
        }
}

我希望当我触发一个事件时,它会被实际存在的观察者接收,而不是创建新的观察者。

所以这里的技巧确实是 @Dependent 具有观察者的 bean 范围。

根据规范,总是会创建新的实例来接收通知并在之后销毁。规范的相关部分是 5.5.6 Invocation of observer methods and 6.4.2 Destruction of objects with scope @Dependent.

解释为什么它的行为是这样的 - 对于其他(正常)范围,您总是在上下文中有该给定 bean 的 0 或 1 个实例,通知可以轻松选择现有的或创建新的并将其存储在语境。而使用 @Dependent 你可以有 0 到 n 个实例,如果行为不同,这将导致每个事件触发多个通知。

尝试使用一些正常范围 (application/request/session/conversation),您应该会得到预期的行为。

我也有类似的情况。下面的想法呢?

@Singleton
public class Observer<E>
{
  private Set<Consumer<E>> consumers = new HashSet<>();
  public void addConsumer(Consumer<E> consumer) { ... }
  void observe(@Oberves E event)
  {
    // propagate event to all consumers
    consumers.forEach(consumer -> consumer.accept(event));
  }
}

您应该能够注入观察者,向其添加消费者,当观察方法传播事件时,消费者将收到通知。

看起来这对我有用。

[edit] 以下是对我的方法的更详细描述: