【问题标题】:Why is my CDI startup class called twice?为什么我的 CDI 启动类被调用了两次?
【发布时间】:2017-10-12 08:15:02
【问题描述】:

在我的 .war 部署到 Payara 后,我正在使用以下类进行一些初始化。而且我可以看到,init() 方法实际上在应用程序启动期间被调用了两次。

package mypackage;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Initialized;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import lombok.extern.log4j.Log4j2;

@Log4j2
@ApplicationScoped
public class StartupService {

    @Inject LogConfig logConfig;

    void init(@Observes @Initialized(ApplicationScoped.class) Object init) {
        log.debug("### STARTUP SERVICE CALLED ###");

        logConfig.startup();
    }
}

有人可以向我解释为什么这会被调用两次以及如何避免它?

我的意思是,我当然可以实现一个静态布尔标志来识别我之前已经被调用过一次,但我更愿意修复根本原因并且只从一开始就被调用一次。

【问题讨论】:

  • 为什么不使用@PostConstruct
  • 这可能与不同的事件负载(CDI 规范,6.7.3 应用程序上下文生命周期)有关 - 你能检查一下负载是什么样的吗?是Object 还是ServletContext(可以使用instanceof)?我敢打赌,它每个一次。如果是这样,我可以进一步详细说明:)
  • @juvenislux 据我所知@ApplicationScoped@PostConstruct 不会导致类在启动期间被急切地加载。您仍然需要“某人”到 @Inject 某个地方的班级才能执行 @PostConstruct
  • @Siliarus 你猜对了,有效载荷不同。在第一次调用时,它是一个 ContextEvent 对象,其中包含“应用程序上下文已初始化”的字符串消息,而在第二次调用时,它是 ApplicationContextFacade 类型的对象。是时候让你进一步阐述了:)

标签: jakarta-ee cdi startup payara


【解决方案1】:

好的,我会尝试总结一下,可能并非完全如此,我可以尝试更深入地挖掘。但这是我认为正在发生的事情......

您会收到两次事件,每次都使用不同的有效负载。 这可能有多种原因,但最有可能的原因是Paraya 在集成方面存在微小缺陷。 Weld 建议集成商通过 SPI 定义其运行的环境。如果他们不这样做,Weld 可以处理它,但需要做一些假设和猜测才能满足规范要求。

在这种情况下,我怀疑 Paraya 错误地定义了 Environment 并且 Weld 不确定要触发哪些事件 - 对于 Web 模块,需要使用 ServletContext 有效负载触发事件,而模块的其余部分具有纯 @ 987654323@ 作为有效载荷。

遗憾的是,除了抢先获取有关环境的信息外,没有简单的方法可以防止这两个事件被触发。原因是 Weld 需要在 Web 模块的任何钩子处于活动状态之前很久就决定 Object 有效负载事件。

您的解决方案可能是将Object 指定为更具体的类型 - 以便仅观察到一个事件。根据您的示例,我假设 ServletContext 有效负载将是您正在寻找的那个。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-02
    • 2011-09-04
    • 1970-01-01
    • 2013-11-20
    • 2019-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多