【发布时间】:2018-02-14 20:57:14
【问题描述】:
我观察到 cdi 事件的一种奇怪行为:
假设以下场景:管理页面上的操作应通过添加“Event-Id”的值来更新网页 news.xhtml 的视图(通过omnifaces):
这两个 bean 都是基于 jsf 的网页的会话范围的支持 bean。
会发生什么: 由于有两个浏览器窗口访问 news.xhtml,因此有两个 NewsBean 实例。当一个事件被触发时,两者 浏览器窗口已更新。但是,这两个更新都来自 NewsBean 第一个实例的@Observes-method。
更准确地说: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
意思是:
- NewsBean 的两个实例都观察到该事件并更新了它们关联的浏览器窗口
- 但是发送到两个浏览器的数据来自第一个实例的@Observes-method
我曾期望每个实例自己的@Observes-method 都会被调用。
Q1:这种行为是有意的吗?
Q2: 是否允许@Observes-method 使用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.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>
【问题讨论】:
-
尝试添加一些登录到观察者方法的方法。我想说只通知一个观察者方法(我希望从 CDI 获得)但您的
channels正在扩大对所有这些视图的更改 -
是的,我在观察者方法中有一个 println,并且服务器日志中只有一个条目(事实上,这就是我注意到这个主题的方式)。这意味着,观察者方法只能使用所有 bean 实例共享的状态。这是一个非常重要的限制。令我恼火的是,CDI 文献中从未提及这一点(例如,Finnigan 关于 Weld 的书对此一无所知)。
-
嗯,CDI 是一个相当庞大的规范,并且集成在许多地方。你不能希望把它全部写下来(还有灰色区域)。除此之外,它很可能写在其他地方……在另一个规范中等等。