代码之家  ›  专栏  ›  技术社区  ›  user120513

CDI:调用哪个实例的@Observes方法?

  •  2
  • user120513  · 技术社区  · 6 年前

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

    给定以下场景:管理页面上的操作将更新网页新闻的视图。xhtml(通过omnifaces)通过添加“事件Id”的值:

    enter image description here

    这两个bean都是其基于jsf的网页的会话范围的支持bean。

    发生的情况: 由于有两个浏览器窗口访问新闻,因此有两个NewsBean实例。xhtml。触发事件时,两个 浏览器窗口已更新。然而 这两个更新都来自 @观察NewsBean的第一个实例的方法 .

    更准确地说:NewsBean有一个属性“id”,该属性在PostConstruct中使用随机 数字此id显示在新闻中。xhtml作为“属性Id”(参见图片)。也会推送id 通过bean的@Observes方法并显示在新闻中。xhtml作为“事件Id”:

    private void onBreakingNews(@Observes Info info) {       
      channel.send(id);  // displayed as "Event-Id" on news.xhtml      
    } 
    

    让这两个实例的id为1和2。更新之前,请浏览新闻窗口。xhtml显示:

    Browser 1:                  Browser 2: 
    
    Property-Id: 1              Property-Id: 2
    Event-Id:                   Event-Id:  
    

    更新后(添加了事件Id值):

    Browser 1:                  Browser 2: 
    
    Property-Id: 1              Property-Id: 2
    Event-Id: 1                 Event-Id: 1 
    

    这意味着:

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

    我原以为会调用每个实例自己的@Observes方法。

    第一季度: 这是故意的吗?

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

    作为cdi容器,我使用WildFly 11的焊接。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 ... 
    }
    

    新闻代码。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 回复  |  直到 6 年前
        1
  •  2
  •   Siliarus    6 年前

    我会尽力回答您的问题:

    问题1:这种行为是故意的吗?

    是的,是的。CDI只选择一次观察者方法(OM),并在通知时使用该方法。在我看来,如果有多个OMs,您将在排序、依赖bean、异常链和上下文线程传播(规范禁止)方面遇到一些严重问题。

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

    当然,这不是一个bug。想象一下 @ApplicationScoped 豆依赖bean的内部状态和/或改变它是非常有意义的。更不用说,要判断observer方法的代码是否访问内部状态几乎是不可能的。

    发生的情况是,当您触发事件时,将在 current context . 例如,当有两个会话时,您将至少有两个上下文。OM将用于在其上下文中触发事件的对象。