EJB 的 CDI 生产者
Posted
技术标签:
【中文标题】EJB 的 CDI 生产者【英文标题】:CDI producer for EJB 【发布时间】:2016-05-30 17:02:44 【问题描述】:我正在尝试使用 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
子句,问题就会消失:有人可以解释一下我的类层次结构有什么问题吗?
【问题讨论】:
@Produces
和 Instance
不能很好地协同工作,请使用其中之一。为什么你需要Instance<ISTGatewayUtil> gtwUtil
而不是ISTGatewayUtil gtwUtil
?
@Geinmachi 因为我希望它被延迟加载:这不是正确的做法吗?
【参考方案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<T>
。您可以注入两个 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
范围内,因为它们没有指定任何范围。
【讨论】:
非常感谢安托万!我知道我必须阅读更多关于 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;
【讨论】:
以上是关于EJB 的 CDI 生产者的主要内容,如果未能解决你的问题,请参考以下文章