我观察到cdi事件的奇怪行为:
鉴于以下情况:管理页面上的操作应通过添加“Event-Id”的值来更新网页news.xhtml的视图(通过omnifaces):
这两个bean都是基于jsf的网页的会话范围的后台bean。
会发生什么:由于两个浏览器窗口访问news.xhtml,因此有两个NewsBean实例。当事件被触发时,两者都有 浏览器窗口已更新。但是,这两个更新都来自于 @ Observes-NewsBean第一个实例的方法。
更确切地说:NewsBean有一个属性“id”,用随机的方式初始化@PostConstruct 数。此id在news.xhtml上显示为“Property-Id”(参见图像)。 id也被推了 通过bean的@ Observes-method并在news.xhtml上显示为“Event-Id”:
private void onBreakingNews(@Observes Info info) {
channel.send(id); // displayed as "Event-Id" on news.xhtml
}
让两个实例的id简单地为1和2.在更新之前,news.xhtml的浏览器窗口显示:
Browser 1: Browser 2:
Property-Id: 1 Property-Id: 2
Event-Id: Event-Id:
更新后(添加了Event-Id值):
Browser 1: Browser 2:
Property-Id: 1 Property-Id: 2
Event-Id: 1 Event-Id: 1
这意味着:
我曾预料到会调用每个实例自己的@ Observes方法。
Q1:这个行为是否打算?
Q2: @ Observes方法是否允许使用bean实例的内部状态? (在这种情况下,这将是一个错误)
作为cdi容器,我使用WildFly 11中的Weld。NewsBean的代码是:
@Named @SessionScoped
public class NewsBean {
private int id;
@Inject @Push(channel="pushChannel") private PushContext channel;
private void onBreakingNews(@Observes Info info) {
// channel.send(info.getMsg());
channel.send(id);
}
@PostConstruct
private void init() {
id = new Random().nextInt(100);
}
... getter and setter for id ...
}
news of news.xhtml:
<h:head>
<f:verbatim>
<script type="text/javascript">
function socketListener(message, channel, event) {
document.getElementById("formId:info").value = message;
};
</script>
</f:verbatim>
</h:head>
<h:body>
<h:outputText value="Property-Id: "/>
<h:outputText value="#{newsBean.id}"/>
<p></p>
<h:form id="formId">
<h:outputText value="Event-Id: "/>
<h:inputText value="" id="info" readonly="true"/>
<o:socket channel="pushChannel" onmessage="socketListener"/>
</h:form>
</h:body>
答案 0 :(得分:2)
我会尽力回答你的问题:
Q1:这种行为是否打算?
是的,确实如此。 CDI只选择一次观察者方法(OM)并在通知时使用它。从我的头脑中,有多个OM,你会引入一些严重的问题,包括排序,依赖bean,异常链和上下文线程传播(规范禁止)。
Q2:@ Observes方法是否允许使用bean实例的内部状态? (在这种情况下,这将是一个错误)
当然,这不是一个错误。想象一下它是@ApplicationScoped
bean。依赖bean的内部状态和/或改变它是完全合理的。更不用说,几乎不可能判断观察者方法的代码是否确实访问内部状态。
当你发射一个事件时,会在current context
内通知观察者。例如,当有两个会话时,您将至少有两个上下文。 OM将用于在其上下文中触发事件的那个。