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 层次结构有什么问题吗?
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;
}
}
}
我正在尝试使用 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 层次结构有什么问题吗?
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;
}
}
}