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 

这意味着:

我原以为会调用每个实例自己的@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 将用于触发事件的上下文。