整合EJB(JNDI)和CDI的最佳方式
The best way to integrate EJB (JNDI) and CDI
目前我们有一个部署架构,其中一堆面向数据的服务通过 RMI 暴露给业务服务。两种类型(面向数据的服务和业务服务)都是无状态会话 bean。每个数据服务接口包(包含远程接口)也有一个定位器,用于执行 JNDI 查找。这样我们就可以从业务服务逻辑中的任何地方调用面向数据的服务。
这是定位器的样子:
public final class OMRLocator {
private static final Logger LOG = Logger.getLogger( OMRLocator.class );
private static final String ORG_WILDFLY_INITIAL_CTX_FACTORY = "org.wildfly.naming.client.WildFlyInitialContextFactory";
private OMRLocator() {
}
@Produces
public static OrganisationsAndMandatesRegister locate() {
try {
Properties ctxProp = new Properties();
ctxProp.put( Context.INITIAL_CONTEXT_FACTORY, ORG_WILDFLY_INITIAL_CTX_FACTORY );
InitialContext ctx = new InitialContext( ctxProp );
return (OrganisationsAndMandatesRegister) ctx.lookup( OrganisationsAndMandatesConstants.REMOTE_NAME );
}
catch ( NamingException ex ) {
LOG.log( Level.WARN, "Cannot reach: " + OrganisationsAndMandatesConstants.REMOTE_NAME, ex );
return null;
}
}
}
我们 运行 在 JBOSS EAP6 上,并开始试验 CDI。因此,我们向数据服务 beans 添加了 beans.xml
和 @Produces
以使(在本例中为 OrganisationAndMandatesRegister
CDI 可注入。我们的想法是未来我们可能会重新打包我们的应用程序并将数据服务与业务服务打包在一个企业档案中。
最近我们迁移到 JBOSS EAP7.2(Wildfly 8?),突然间我们看到了各种意想不到的延迟和事务问题。
我怀疑我们获取 bean 的方式是造成这些问题的一个因素。例如:我猜范围取决于业务 EJB 生命周期,但是对于业务服务中对 locate()
的每次调用,都会生成一个新的数据服务实例。
那么:使用 CDI 时生成远程 bean(通过 RMI)的最佳方法是什么?鉴于这两种类型的服务都是无状态的(或者这是自动完成的),我是否应该考虑范围界定?
如果在生产者方法上没有定义范围,则使用@Dependend,因此找到合适的范围,可能是@RequestScoped。当您从 JNDI 检索 EJB 时,您不会获得新的实例,而是从池中获得一个实例,该实例在多次调用中可能是相同的。您的问题可能是 EJB 拦截器,因为如果依赖作用域,EJB 实例在注入后始终相同,并且永远不会释放。
摆脱@Produces,因为CDI 与EJB 集成并且EJB 可以通过@Inject 或@EJB 注入。如果您想保留 Locator class,那么您可以在其中注入 EJB 并 return 正确的 EJB 实例(实际上是一个代理),其中 Locator 应该是@ApplicationScoped。另一种方法是使用允许编程查找的实例。使用 Object 类型,您可以访问容器的所有 CDI Bean(包括 EJB),因此通用接口将有助于最大限度地减少可访问的 bean。
查看以下链接以获得更多帮助。
Inject a stateless EJB with @Inject into CDI Weld ManagedBean (JSF 1.2 EJB Application on jboss 6 AS)
http://www.adam-bien.com/roller/abien/entry/inject_vs_ejb
总结一下:
选项 a) 保持原样。也许使用 @Dependent
明确范围以指示这是在调用 bean 创建时调用的(注入调用 bean 的构造函数)
选项 b) 使用无状态 @ApplicationScoped
会话 bean
@LocalBean // otherwise @EJB will not work
@ApplicationScoped // this instance should be created only once per ear
public class OMRLocator {
@EJB // does implicitly a remote (default) JNDI lookup
private OrganisationsAndMandatesRegister instance;
@Produces
@Dependent // just to make it explicit
public OrganisationsAndMandatesRegister locate() {
return instance;
}
}
目前我们有一个部署架构,其中一堆面向数据的服务通过 RMI 暴露给业务服务。两种类型(面向数据的服务和业务服务)都是无状态会话 bean。每个数据服务接口包(包含远程接口)也有一个定位器,用于执行 JNDI 查找。这样我们就可以从业务服务逻辑中的任何地方调用面向数据的服务。
这是定位器的样子:
public final class OMRLocator {
private static final Logger LOG = Logger.getLogger( OMRLocator.class );
private static final String ORG_WILDFLY_INITIAL_CTX_FACTORY = "org.wildfly.naming.client.WildFlyInitialContextFactory";
private OMRLocator() {
}
@Produces
public static OrganisationsAndMandatesRegister locate() {
try {
Properties ctxProp = new Properties();
ctxProp.put( Context.INITIAL_CONTEXT_FACTORY, ORG_WILDFLY_INITIAL_CTX_FACTORY );
InitialContext ctx = new InitialContext( ctxProp );
return (OrganisationsAndMandatesRegister) ctx.lookup( OrganisationsAndMandatesConstants.REMOTE_NAME );
}
catch ( NamingException ex ) {
LOG.log( Level.WARN, "Cannot reach: " + OrganisationsAndMandatesConstants.REMOTE_NAME, ex );
return null;
}
}
}
我们 运行 在 JBOSS EAP6 上,并开始试验 CDI。因此,我们向数据服务 beans 添加了 beans.xml
和 @Produces
以使(在本例中为 OrganisationAndMandatesRegister
CDI 可注入。我们的想法是未来我们可能会重新打包我们的应用程序并将数据服务与业务服务打包在一个企业档案中。
最近我们迁移到 JBOSS EAP7.2(Wildfly 8?),突然间我们看到了各种意想不到的延迟和事务问题。
我怀疑我们获取 bean 的方式是造成这些问题的一个因素。例如:我猜范围取决于业务 EJB 生命周期,但是对于业务服务中对 locate()
的每次调用,都会生成一个新的数据服务实例。
那么:使用 CDI 时生成远程 bean(通过 RMI)的最佳方法是什么?鉴于这两种类型的服务都是无状态的(或者这是自动完成的),我是否应该考虑范围界定?
如果在生产者方法上没有定义范围,则使用@Dependend,因此找到合适的范围,可能是@RequestScoped。当您从 JNDI 检索 EJB 时,您不会获得新的实例,而是从池中获得一个实例,该实例在多次调用中可能是相同的。您的问题可能是 EJB 拦截器,因为如果依赖作用域,EJB 实例在注入后始终相同,并且永远不会释放。
摆脱@Produces,因为CDI 与EJB 集成并且EJB 可以通过@Inject 或@EJB 注入。如果您想保留 Locator class,那么您可以在其中注入 EJB 并 return 正确的 EJB 实例(实际上是一个代理),其中 Locator 应该是@ApplicationScoped。另一种方法是使用允许编程查找的实例。使用 Object 类型,您可以访问容器的所有 CDI Bean(包括 EJB),因此通用接口将有助于最大限度地减少可访问的 bean。
查看以下链接以获得更多帮助。
Inject a stateless EJB with @Inject into CDI Weld ManagedBean (JSF 1.2 EJB Application on jboss 6 AS)
http://www.adam-bien.com/roller/abien/entry/inject_vs_ejb
总结一下:
选项 a) 保持原样。也许使用 @Dependent
明确范围以指示这是在调用 bean 创建时调用的(注入调用 bean 的构造函数)
选项 b) 使用无状态 @ApplicationScoped
会话 bean
@LocalBean // otherwise @EJB will not work
@ApplicationScoped // this instance should be created only once per ear
public class OMRLocator {
@EJB // does implicitly a remote (default) JNDI lookup
private OrganisationsAndMandatesRegister instance;
@Produces
@Dependent // just to make it explicit
public OrganisationsAndMandatesRegister locate() {
return instance;
}
}