CDI:调用了哪个实例的@ Observes-method?

时间:2018-02-14 20:57:15

标签: cdi weld

我观察到cdi事件的奇怪行为:

鉴于以下情况:管理页面上的操作应通过添加“Event-Id”的值来更新网页news.xhtml的视图(通过omnifaces):

enter image description here

这两个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 

这意味着:

  • NewsBean的两个实例都观察了该事件并更新了其关联的浏览器窗口
  • 但发送到两个浏览器的数据来自第一个实例的@ Observes方法

我曾预料到会调用每个实例自己的@ 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> 

1 个答案:

答案 0 :(得分:2)

我会尽力回答你的问题:

  

Q1:这种行为是否打算?

是的,确实如此。 CDI只选择一次观察者方法(OM)并在通知时使用它。从我的头脑中,有多个OM,你会引入一些严重的问题,包括排序,依赖bean,异常链和上下文线程传播(规范禁止)。

  

Q2:@ Observes方法是否允许使用bean实例的内部状态? (在这种情况下,这将是一个错误)

当然,这不是一个错误。想象一下它是@ApplicationScoped bean。依赖bean的内部状态和/或改变它是完全合理的。更不用说,几乎不可能判断观察者方法的代码是否确实访问内部状态。

当你发射一个事件时,会在current context内通知观察者。例如,当有两个会话时,您将至少有两个上下文。 OM将用于在其上下文中触发事件的那个。