【问题标题】:CDI conditional Bean with Instance<T>带有 Instance<T> 的 CDI 条件 Bean
【发布时间】:2018-10-01 18:28:58
【问题描述】:

我有必要在运行时提供正确的 Bean 实现。

通用接口:

public interface MyInterface { ... }

实现:

@Named("one")
class MyInterfaceImpl1 implements MyInterface { ... }

@Named("two")
class MyInterfaceImpl2 implements MyInterface { ... }

@Named("three")
class MyInterfaceImpl3 implements MyInterface { ... }

注意这些类是包私有的。

然后我写了一个@Produces方法:

@Produces
@Singleton
MyInterface getMyInterface(
        final Instance<MyInterface> myInterfaceImplementations,
        final Configuration configuration) {
    // Might be one, two or three.
    final String parameter = configuration.getString("value");
    return myInterfaceImplementations.select(new NamedLiteral(parameter)).get();
}

这是正确的方法,还是有更好的解决方案?

【问题讨论】:

  • 您的解决方案似乎很好,除了 2 个细节:(1)您确定 @Singleton 是否符合您的预期?我会使用@ApplicationScoped。 (2) 如果生成的 bean 确实打算成为单例,那么您正在无缘无故地创建 N-1 个 bean(除非在其他地方按名称使用)。这可能严重或不严重,取决于未使用的 bean 的“重量”程度。
  • @NikosParaskevopoulos 嗨!是的,我真的不需要代理对象,我只需要容器为每个 JVM 实例化一次。对于(2)点,我想我没有明白,你能详细说明一下吗?谢谢!嗯,你是说每个注入点都会调用 Producer 方法,即使它被标记为 Singleton?

标签: java cdi


【解决方案1】:

您的解决方案可以正常工作,这里是我的 0.02$ 只是为了确保您是这样打算的:

Nikos Paraskevopoulos 在他的评论中的意思是,您实际上是在创建四个 bean 来注入一个。 MyInterfaceImpl1MyInterfaceImpl2MyInterfaceImpl3 都是合法的 bean,可以在应用程序的任何地方进行注入。如果这些 bean 很重,创建可能需要一些时间,也可能不打算在任何地方注入它们?然后是你的生产者方法——第四个 bean——我认为它最终是你唯一需要的。

其次,这三个实现 bean 的范围与生产者方法不同。如果他们有资格注入,在你的情况下,他们可能共享相同的范围似乎是合乎逻辑的?

第三,使用@Singleton。我还建议@ApplicationScoped,拥有代理不会造成伤害,也不会产生开销。您将无法区分,并且可以轻松避免 CDI 单例(其行为不像 EJB 单例)的一些令人不快的意外。

【讨论】:

  • 是的,准确。我不想吃不必要的豆子。我只需要一个实现,作为单例,就可以在容器周围。您能否详细说明如何避免这种情况?谢谢!请记住,我需要将这些类维护为包私有,并且生产者方法无法访问它们。
  • 如果你需要类似单例的行为,那就是@ApplicationScoped。所以你不能改变实际的 impl 类?在这种情况下(只要它是私有包并且生产者在其他地方),您的解决方案似乎是唯一干净的解决方案。也许您可以尝试将生产者作为启用的替代方案,以确保任何MyInterface 的注入都通过生产者方法路由?
  • 路由替代方案听起来不错。您在考虑替代注释吗?像用 Alternative 注释每个 Impl 之类的东西?
  • 我在想相反的方式-创建生产者方法@Alternative并添加@Priority(x)以启用它。这样任何@Inject MyInterface 都将使用生产者。
  • 我在 CDI 1.1 上,优先级仅适用于类型。但是谢谢你的想法,如果我能让他们升级到 Java EE 8
【解决方案2】:

我认为更优雅的解决方案是让 CDI 发挥所有作用 ;-) 比如:

import javax.enterprise.inject.spi.CDI;

@Produces
@Singleton
MyInterface getMyInterface(final Configuration configuration) {
    // Might be one, two or three.
    final String parameter = configuration.getString("value");
    Set<MyInterface> candidates = CDI.current().getBeanManager().getBeans(parameter);
    return ( candidates.size()>0 ? candidates.get(0) : null);
}

在寻找接口的特定 impl 时,您还可以使用 getBeans() 签名的替代签名来使用限定符: cfrhttps://docs.oracle.com/javaee/7/api/javax/enterprise/inject/spi/BeanManager.html#getBeans-java.lang.reflect.Type-java.lang.annotation.Annotation...-

【讨论】:

  • 所以基本上我应该让实现类保持原样并通过 BeanManager 获得正确的类。有什么不同?我的意思是,Instance 对象是由 CDI 本身创建的
  • 你不再需要你的工厂类(myInterfaceImplementations)。
  • 但这不是“我的”工厂,而是 CDI 可注入实例类:docs.jboss.org/cdi/api/1.1/javax/enterprise/inject/… 这就是我问的原因
猜你喜欢
  • 2016-01-12
  • 2018-07-31
  • 2013-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多