EJB 的 CDI 生产者

CDI producer for EJB

我正在尝试使用 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 时都遇到了问题。

编辑

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

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

引用 specification

3.2.2. Bean types of a session bean

The unrestricted set of bean types for a session bean contains all local interfaces of the bean and their superinterfaces. If the session bean has a no-interface view, the unrestricted set of bean types contains the bean class and all superclasses. In addition, java.lang.Object is a bean type of every session bean.

Remote interfaces are not included in the set of bean types.

因此,由于您的两个会话 bean 都有一个本地接口,因此它们的 bean 类型集中没有 classes。因此,您无法使用 class.

解决它们是正常的

您需要向会话 bean 定义添加额外信息,以便能够区分它们或将它们声明为带有 @LocalBean 注释的无接口视图 EJB。
这是将 EJB 声明为 NIV

的新版本代码
@Stateless
@LocalBean 
public class STGatewayMQTTUtil implements Serializable, ISTGatewayUtil {
    ...
}

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

您的生产者不需要注入 2 Instances<T>。您可以同时注入两个 bean 和 return 选择的 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<T>

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 范围内,因为它们没有指定任何范围。

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

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

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

   ISTGatewayType value()

   enum ISTGatewayType {
       MQT,
       WS
   }
}

用法如下所示:(注意 ejbs 已经用 @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;
       }
    }  
}