【问题标题】:CDI observer not yet initialized when the event is fired触发事件时,CDI 观察者尚未初始化
【发布时间】:2015-06-11 21:21:15
【问题描述】:

我有两个 SLSB:

  • BeanF,
  • BeanO

在两个 ejb-jars 中:

  • ModF,
  • ModO

BeanF 触发一个事件,BeanO 观察它。

第一个fire(-) 操作以异常结束(Wildfly 8.2):

错误 [org.jboss.as.ejb3.invocation] JBAS014134: EJB 调用在组件 BeanF 上失败,用于方法 public void BeanF.publish(ModEvent):
javax.ejb.EJBException: org.jboss.msc.service.ServiceNotFoundException: 服务服务
jboss.deployment.subunit."myapp.ear"."modO.jar".component.BeanO.VIEW."BeanO".LOCAL 未找到

进一步的fire(-) 操作到达观察者,但我不能让任何事件丢失。

有没有办法在事件触发之前(或在事件触发并等待处理之后即时)强制初始化观察者?

@Observes(notifyObserver = Reception.IF_EXISTS) 仅允许在观察者尚未准备好时以静默方式跳过事件。
在我的情况下,BeanF 不能依赖于BeanO,因为必须在application.xml 中的ModF 之后声明ModO

是否可以使用 CDI 事件/侦听器,或者我需要使用 JMS?

【问题讨论】:

  • 你能从 SLSB 切换到 Singletons 吗?那么使用@Sturtup 可以解决问题吗?
  • Reception.IF_EXISTS 主要适用于范围更广的bean(如 SessionScope 、 ApplicationScoped 等)。如果您的 bean 是 @Stateless,则仅为单个调用“创建”实例,这使得 IF_EXISTS 几乎总是返回 false(除非事件触发和观察者位于同一堆栈跟踪中)。
  • 尝试使用 @Singleton@Startup 注释两个 bean 并收到相同的异常(我无法使用 @DependsOn( BeanO ) 注释 BeanF,因为 ModF 看不到 ModO)。还尝试将BeanO 更改为@ApplicationScoped,在这种情况下,可以接收到事件-BeanO.observe(@Observes ...) 方法开始执行,执行了一些日志记录,但在尝试调用其他一些@Stateless bean 时因ServiceNotFoundException 异常而崩溃(这其他 bean 必须是 @Stateless,因为它使用了 TransactionAttributeType.REQUIRES_NEW 注释)。

标签: events cdi wildfly java-ee-7 ejb-jar.xml


【解决方案1】:

其他实验

尝试用@Singleton@Startup 注释两个bean 并收到相同的异常(我不能用@DependsOn( BeanO ) 注释BeanF,因为ModF 看不到ModO)。 还尝试将BeanO 更改为@ApplicationScoped,在这种情况下,可以接收到事件-BeanO.observe(@Observes ...) 方法开始执行,执行了一些日志记录,但在尝试调用其他一些@Stateless bean 时因ServiceNotFoundException 异常而崩溃(这其他 bean 必须是 @Stateless,因为它使用了 TransactionAttributeType.REQUIRES_NEW 注释)。

CDI 解决方案

有点难看但可行的解决方案:缓存/排队事件:将 BeanO 拆分为两个 bean:BeanO1BeanO2。让BeanO1 成为@ApplicationScoped bean,让它观察事件并通过调用一些空心方法并捕获ServiceNotFoundException 来检测BeanO2 是否准备好。如果BeanO2 不存在,则该事件在ConcurrentLinkedQueue 中的BeanO1 中排队。 BeanO2 是无状态的,除了观察之外,它会做 BeanO 所做的一切。当一个事件到达并且BeanO2 准备好时,BeanO1 首先从队列中推送事件。只有当有一些其他事件会实际触发处理旧事件时,这才是可接受的。

JMS

我想使用 JMS 将是最干净的解决方案,但也有一些陷阱:

  • 如果使用topic,那么我们可能会再次陷入最初的问题 - 订阅者在发送第一条消息后注册(不确定规范,但如果没有订阅者,消息会丢失)。李>
  • 如果使用queue,那么当ModF 用于其他产品并且没有ModO 时可能会出现问题 - 队列会膨胀,这可能不是超级直觉。
    • 也许ModF 可以定义一些MDB 来读取所有事件并丢弃它们,ModO 以某种方式注册了自己的具有更高优先级的MDB(只是猜测,不知道这是否可能)
    • 也许ModO 可以包含一个带有队列名称的配置文本文件,因此ModF 可以读取该文件,即时初始化与队列的连接并将事件放入此队列(如果没有ModO 并且有没有文件,则ModF 不会触发事件。

【讨论】:

    【解决方案2】:

    首先,我认为 CDI 创建了一个 bean 的“新”实例并将事件传递给它以防万一

    • 没有活动的上下文实例(在当前范围内),并且
    • 观察者不是有条件的

    我写过这个here

    所以这种行为在 IMO 很奇怪

    就替代方案而言 - 是否可以将您的观察者方法声明为“静态”?在这种情况下,CDI 肯定会调用它

    另外,如果您还没有阅读过,我强烈建议您阅读 CDI 规范文档(第 10 章和第 5.5.6 节)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-12-14
      • 2016-11-01
      • 1970-01-01
      • 2017-05-30
      • 2011-04-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多