CDI:调用了哪个实例的@Observes-method?
CDI: Which instance's @Observes-method is called ?
我观察到 cdi 事件的奇怪行为:
给定以下场景:管理页面上的操作应通过添加 "Event-Id" 的值来更新网页 news.xhtml 的视图(通过 omnifaces):
这两个 bean 都是基于 jsf 的网页的会话范围的支持 beans。
What happens: There are two instances of NewsBean due to two browser windows accessing news.xhtml. When an event is fired, both
browser windows are updated. However, both updates come from the
@Observes-method of the first instance of NewsBean.
更准确地说:NewsBean 有一个 属性 "id" 被 @PostConstruct 随机初始化
数字。此 id 在 news.xhtml 上显示为 "Property-Id"(参见图片)。 id也被推送
通过 bean 的 @Observes 方法并在 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 的浏览器 windows 显示:
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 实例都观察到了该事件并更新了它们关联的浏览器 window
- 但是发送到两个浏览器的数据来自第一个实例的@Observes-方法
我原以为会调用每个实例自己的@Observes 方法。
Q1: Is this behavoir intended ?
Q2: Is the @Observes-method allowed to use internal state of the bean instance ? (in this case it would be a bug)
作为 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>
我会尝试给出一些答案来解决您的问题:
Q1: Is this behavior intended ?
是的,是的。 CDI 只选择一次观察者方法 (OM) 并在通知时使用它。从我的头脑来看,拥有多个 OM,您会在排序、依赖 bean、异常链和上下文线程传播(规范禁止)方面引入一些严重的问题。
Q2: Is the @Observes-method allowed to use internal state of the bean instance ? (in this case it would be a bug)
当然,这不是一个错误。想象一下它是 @ApplicationScoped
个豆子。依靠 bean 的内部状态 and/or 来改变它是完全合理的。更不用说判断观察者方法的代码是否访问内部状态几乎是不可能的。
发生的事情是,当您触发一个事件时,观察者将在您的 current context
中收到通知。例如,当有两个会话时,您将至少有两个上下文。 OM 将用于触发事件的上下文。
我观察到 cdi 事件的奇怪行为:
给定以下场景:管理页面上的操作应通过添加 "Event-Id" 的值来更新网页 news.xhtml 的视图(通过 omnifaces):
这两个 bean 都是基于 jsf 的网页的会话范围的支持 beans。
What happens: There are two instances of NewsBean due to two browser windows accessing news.xhtml. When an event is fired, both browser windows are updated. However, both updates come from the @Observes-method of the first instance of NewsBean.
更准确地说:NewsBean 有一个 属性 "id" 被 @PostConstruct 随机初始化 数字。此 id 在 news.xhtml 上显示为 "Property-Id"(参见图片)。 id也被推送 通过 bean 的 @Observes 方法并在 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 的浏览器 windows 显示:
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 实例都观察到了该事件并更新了它们关联的浏览器 window
- 但是发送到两个浏览器的数据来自第一个实例的@Observes-方法
我原以为会调用每个实例自己的@Observes 方法。
Q1: Is this behavoir intended ?
Q2: Is the @Observes-method allowed to use internal state of the bean instance ? (in this case it would be a bug)
作为 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>
我会尝试给出一些答案来解决您的问题:
Q1: Is this behavior intended ?
是的,是的。 CDI 只选择一次观察者方法 (OM) 并在通知时使用它。从我的头脑来看,拥有多个 OM,您会在排序、依赖 bean、异常链和上下文线程传播(规范禁止)方面引入一些严重的问题。
Q2: Is the @Observes-method allowed to use internal state of the bean instance ? (in this case it would be a bug)
当然,这不是一个错误。想象一下它是 @ApplicationScoped
个豆子。依靠 bean 的内部状态 and/or 来改变它是完全合理的。更不用说判断观察者方法的代码是否访问内部状态几乎是不可能的。
发生的事情是,当您触发一个事件时,观察者将在您的 current context
中收到通知。例如,当有两个会话时,您将至少有两个上下文。 OM 将用于触发事件的上下文。