我正在用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));
}
}
我希望当我触发一个事件时,它会被实际的现有观察者接收,而不是创建新的观察者。
答案 0 :(得分:0)
因此,这里的窍门确实是具有观察者的bean的@Dependent
范围。
根据规范,总是会创建一个新实例来接收通知并随后销毁。规范的相关部分是5.5.6 Invocation of observer methods和6.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