急切的 CDI bean 实例化的干净解决方案

Clean solution for eager CDI bean instantiation

设想以下简化的 DI 模型:

@ApplicationScoped
public class A {

    private B b;

    @Inject
    public A(B b) {
        this.B = b;
    }

}

@ApplicationScoped
public class B {

    private C c;

    @Inject
    public B(C c) {
        this.C = c;
    }

}

@ApplicationScoped
public class C {

    @PostConstruct
    public void start() {
        // processing that should begin on startup
    }

}

假设我希望在部署完成时调用 C#start。通常在线建议的模式是 here 提供的模式,但该解决方案:1) 添加了太多样板,2) 为扩展添加了一个新的文本文件,3) 求助于使用 toString 仅触发 B 代理来实例化实际的 B bean,其下将依次触发 C 代理等

从CDI 1.1开始,在A中加入如下方法也是一种解决方法:

public void init(@Observes @Initialized(ApplicationScoped.class) Object init) {
   B.toString();
}

这解决了上面描述的前两个问题,但我仍然需要在 B 上调用一个虚拟方法,以便触发 instantiation/injection 链并最终调用带注释的 @PostConstruct C.

的方法

我是否缺少针对此问题的更简洁的解决方案? CDI 2.0 是否解决了这个问题?

没有选项可以 select 进行预初始化,您必须选择一些 "workaround"。 CDI 没有定义 bean init 应该是惰性的还是急切的,并且由于大多数时候惰性更有意义,Weld 就是那样。

您最好的选择与 article you mentioned 所建议的类似。例如。设置一个扩展,挑选你想要急切初始化的 beans 并初始化它们并在它们上调用一个(无害的)方法。

显然,这仅对部署中的少数 bean 有意义(我认为最多是应用范围的 beans),因此不会有太多开销(如果有的话)。有一些额外的样板文件,但并不是说你必须写那么多行才能完成这项工作。

为了让它更好,您可以让所有 bean 使用默认 ping() 方法实现一个虚拟接口,并从扩展中调用该方法 - 这样您就可以避免调用 toString().