【问题标题】:CDI producer for EJBEJB 的 CDI 生产者
【发布时间】:2023-03-29 01:25:02
【问题描述】:

我正在尝试使用 POJO 作为 CDI 生产者来注入正确的 EJB,但我得到 org.jboss.weld.exceptions.UnsatisfiedResolutionException: WELD-001308

这是我的制作人 POJO

public class STGatewayUtilProducer {

    @Produces
    @Chosen
    public ISTGatewayUtil getISTGatewayUtil(Instance<STGatewayWSUtil> ws, Instance<STGatewayMQTTUtil> mqtt, ConfigurationManager cm) {
        switch(cm.getGatewayProtocol()) {
            case ConfigurationManager.GATEWAY_PROTOCOL_TYPE_MQTT:
                return mqtt.get();
            default:
                return ws.get();
        }
    }

}

这是限定符定义:

@Qualifier
@Target({ TYPE, METHOD, PARAMETER, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Chosen {}

这些是 EJB 声明:

@Stateless
public class STGatewayMQTTUtil implements Serializable, ISTGatewayUtil {
    ...
}

@Stateless
public class STGatewayWSUtil implements Serializable, ISTGatewayUtil {
    ...
}

最后,这是我注入 EJB 的方式:

@Inject
@Chosen
private Instance<ISTGatewayUtil> gtwUtil;

我在使用 JBoss AS 7 和 WildFly 10 时都遇到了问题。

编辑

我找到了问题的核心!我声明了一个通用的 abstract 父类,它实现了 ejb 接口并让我的会话 bean 扩展它:使用这种结构无法解析 bean。

相反,如果我在会话 bean 上移动 implements 子句,问题就会消失:有人可以解释一下我的类层次结构有什么问题吗?

【问题讨论】:

  • @ProducesInstance 不能很好地协同工作,请使用其中之一。为什么你需要Instance&lt;ISTGatewayUtil&gt; gtwUtil 而不是ISTGatewayUtil gtwUtil
  • @Geinmachi 因为我希望它被延迟加载:这不是正确的做法吗?

标签: jakarta-ee jboss ejb cdi wildfly


【解决方案1】:

引用specification

3.2.2。会话 bean 的 bean 类型

会话 bean 的不受限制的 bean 类型集包含所有 bean 的本地接口及其超接口。如果会话 bean 有一个无接口视图,无限制的 bean 类型集 包含 bean 类和所有超类。此外, java.lang.Object 是每个会话 bean 的 bean 类型。

远程接口不包含在 bean 类型集中。

因此,由于您的两个会话 bean 都有本地接口,因此它们的 bean 类型集中没有它们的类。因此,您无法通过他们的类解决它们是正常的。

您需要在会话 bean 定义中添加额外的信息,以便能够区分它们或使用 @LocalBean 注释将它们声明为无接口视图 EJB。
这是您的代码的新版本,将您的 EJB 声明为 NIV

@Stateless
@LocalBean 
public class STGatewayMQTTUtil implements Serializable, ISTGatewayUtil {
    ...
}

@Stateless
@LocalBean
public class STGatewayWSUtil implements Serializable, ISTGatewayUtil {
    ...
}

您的生产者不需要注入 2 Instances&lt;T&gt;。您可以注入两个 bean 并返回选择的一个。

public class STGatewayUtilProducer {

    @Produces
    @Chosen
    public ISTGatewayUtil getISTGatewayUtil(STGatewayWSUtil ws, STGatewayMQTTUtil mqtt, ConfigurationManager cm) {
        switch(cm.getGatewayProtocol()) {
            case ConfigurationManager.GATEWAY_PROTOCOL_TYPE_MQTT:
                return mqtt;
            default:
                return ws;
        }
    }

}

或像这样使用Instance&lt;T&gt;

public class STGatewayUtilProducer {

    @Produces
    @Chosen
    public ISTGatewayUtil getISTGatewayUtil(Instance<ISTGatewayUtil> gw, ConfigurationManager cm) {
        switch(cm.getGatewayProtocol()) {
            case ConfigurationManager.GATEWAY_PROTOCOL_TYPE_MQTT:
                return gw.select(STGatewayMQTTUtil.class).get();
            default:
                return gw.select(STGatewayWSUtil.class).get();
        }
    }

}

使用 bean 实例生成新 bean 时要小心,它应该具有 @Dependent 范围(以避免在生成的 bean 上叠加 2 个生命周期)或注入 @New 关键字。在这里,您的会话 bean 位于 @Dependent 范围内,因为它们没有指定任何范围。

【讨论】:

  • 非常感谢安托万!我知道我必须阅读更多关于 EJB 视图的信息,因为它对我来说不是很清楚......我尝试使用本地接口 EJB 而不是 NIV 从两个类中删除 @LocalBean 并将 @Local 放在接口 @987654333 上@ 但它抛出了同样的异常:你能解释一下为什么吗?另外,我不明白您关于叠加 2 个生命周期的最后警告):您能否进一步解释一下或链接我其他阅读内容?
  • 如果您从会话 bean 中删除 @LocalBean,您将返回到之前的用例。将@Local 添加到接口只会使其显式。我不是 EJB 专家(仅 CDI 规范负责人),您可以查看此博客文章以获取有关 EJB 视图的更多信息:piotrnowicki.com/2013/03/…
  • 如果你在@RequestScoped 中声明一个CDI bean,它的一个给定实例将只存在于http 请求的时间。这个“生死”(生命周期)是由 CDI 容器自动管理的。如果您在 @ApplicationScoped bean 的生产者中注入 @RequestScoped bean(通过示例中的参数),并且您使用该 bean 的全部或部分来创建您的 @ApplicationScoped bean,您将获得奇怪的结果,因为两个bean都在不同的范围内。 @New 是为此而引入的,但自 CDI 1.1 起,它已被弃用,我们更喜欢将 @Dependent bean 用于这种用例
  • 我理解了混合生命周期的问题。相反,我还不清楚我应该如何将 CDI 生产者与仅公开本地接口的会话 bean 一起使用。或者生产者只能用于声明(也)@LocalBean?
  • 不是生产者的问题,而是bean解析的问题。当 EJB 与 CDI 一起使用时,除非它是无接口视图 (NIV) EJB(使用 @LocalBean 定义),否则在解析注入点时不会考虑其类。在这里,您的 2 个 EJB 不是(NIV)EJB 并且实现了相同的接口ISTGatewayUtil,因此没有任何方法可以区分它们。可以通过在 EJB 上引入 2 个接口或使用 2 个不同的限定符来区分。
【解决方案2】:

您的案例场景非常适用于 CDI 限定符,如果您需要事务管理,您仍然可以维护 ejb 会话 bean(如果您不需要任何事务逻辑,那么我会首先取消 ejb )。

也就是说,我会这样设计你的场景:

@Qualifier
@Retention(RUNTIME)
@Target(FIELD, METHOD, PARAMETER, TYPE)
public @interface ISTGateway {

   ISTGatewayType value()

   enum ISTGatewayType {
       MQT,
       WS
   }
}

用法如下所示:(注意 ejb 已用 @Dependent 注释以使 CDI 容器自动检测它们)

@Stateless
@Dependent 
@ISTGateway(MQT)
public class STGatewayMQTTUtil implements Serializable, ISTGatewayUtil {
    ...
}

@Stateless
@Dependent
@ISTGateway(WS)
public class STGatewayWSUtil implements Serializable, ISTGatewayUtil {
    ...
}

你的生产者应该是这样的:(这里的生产者的好处是你永远不需要更新它,如果你添加了一个新的 ISTGatewayUtil)

@ApplicationScoped
public class STGatewayUtilProducer {
    @Any
    @Inject
    private Instance<ISTGatewayUtil> istGatewayUtils;

    @Inject
    private ConfigurationManager configurationManager;

    @Chosen
    @Produces
    public ISTGatewayUtil getISTGatewayUtil() {
        final ISTGateway istGateway = new ISTGatewayImpl(cm.getGatewayProtocol());
        return istGatewayUtils.select(istGateway).get();
    }

    private static final class ISTGatewayImpl extends AnnotationLiteral<ISTGateway> implements ISTGateway {

       private final ISTGatewayType istGatewayType;

       private ISTGatewayImpl( final ISTGatewayType istGatewayType) {
          this.istGatewayType = istGatewayType;
       }

       public ISTGatewayType value() {
           return istGatewayType;
       }
    }  
}

【讨论】:

    猜你喜欢
    • 2015-04-08
    • 1970-01-01
    • 2022-10-12
    • 2013-09-17
    • 1970-01-01
    • 2017-09-04
    • 1970-01-01
    • 2015-10-01
    • 1970-01-01
    相关资源
    最近更新 更多