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

时间:2019-06-14 18:17:32

标签: java javafx cdi

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

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

看来我只是不了解fire()的正确语义。从fire()开始创建观察者?可能与@Dependent范围有关。

当某个属性在某处发生更改时,我做了一个拦截器来发送事件(在我的情况下,这是一个更新属性Progress的后台任务)。 (不要关注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();
        }
    }
}

这是我的Task类中的设置器:

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

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

2 个答案:

答案 0 :(得分:0)

因此,这里的窍门确实是具有观察者的bean的@Dependent范围。

根据规范,总是会创建一个新实例来接收通知并随后销毁。规范的相关部分是5.5.6 Invocation of observer methods6.4.2 Destruction of objects with scope @Dependent

要解释为什么会这样-对于其他(普通)作用域,您在上下文中始终有0或1个给定bean的实例,并且通知可以轻松选择现有的bean或创建新的bean或将其存储在上下文中。而使用@Dependent则可以有0到n个实例,如果行为不同,则会导致每个事件触发多个通知。

尝试使用一些常规范围(应用程序/请求/会话/会话),您应该会获得预期的行为。

答案 1 :(得分:0)

我也遇到过类似的情况。下面的想法怎么样?

@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] 以下是我的方法的更详细描述:observing multiple events in one and the same observer instance