【问题标题】:Accessing an EJB3 bean in a jar from an independently deployed war (not packaged in ear)从独立部署的战争中访问 jar 中的 EJB3 bean(未打包在 ear 中)
【发布时间】:2012-08-09 16:01:39
【问题描述】:

出于某些原因,我想将我的应用程序部署为两个单独的工件:Users-ejb.jarUsers-war.war,没有打包在同一个工件中ear(但仍然部署在相同 JBoss AS 7.1 实例中)。在 Users-war.war 中,我有一个支持 bean(注释为 JSF 托管 bean),我希望在其中注入一个封装在 Users-ejb.jar 中的 EJB3。简单的 @EJB 注入在 Users-ejb.jar 和>Users-war.war单独部署。

我的设置的缩小简化示例如下:

EJB3 bean

import javax.ejb.*;

(...)

@Stateless(name="userFacade")
@Local(IUserFacadeLocal.class)
@Remote(IUserFacadeRemote.class)
public class UserFacade extends AbstractFacade<User> implements IUserFacadeLocal, IUserFacadeRemote {

支持 bean

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.ejb.EJB;

import entities.User;
import facades.IUserFacadeRemote;
import facades.IUserFacadeLocal;

@ManagedBean(name="indexBackingBean")
@SessionScoped
public class IndexBackingBean implements Serializable {

    @EJB(beanName="userFacade")
    private IUserFacadeLocal userFacade;

我尝试了各种组合,例如将支持 bean 中的 EJB3 bean 的类型声明为 IUserFacadeRemote(而不是 IUserFacadeLocal),但是当 Users-war.war 模块已部署:

Caused by: org.jboss.as.server.deployment.DeploymentUnitProcessingException:
JBAS014543: No EJB found with interface of type 'facades.IUserFacadeLocal' and
 name 'userFacade' for binding controllers.IndexBackingBean/userFacade

Users-ejb.jar 部署到 JBoss AS 7.1 没有任何抱怨,但是当 Users-war.war 部署时,JBoss 抱怨它不能找到他应该注射的豆子。

但是,我可以使用 JNDI 获得对 EJB3 bean 的引用:

String jndiName = "java:global/Users-ejb/userFacade!facades.IUserFacadeRemote";
this.userFacade = (IUserFacadeRemote) new InitialContext().lookup(jndiName);

尽管如此,@EJB 注入似乎不起作用。

更新: 我遵循了Tom Anderson 给出的以下建议,并且有效的注入是:

@EJB(mappedName = "java:global/Users-ejb/userFacade!facades.IUserFacadeRemote")

如果我理解正确,它使用供应商特定的 mappedName 属性。我无法让注入以独立于供应商的方式工作。

【问题讨论】:

  • 看看这个example。它适用于 GlassFish,但也许可以帮助...
  • 我的建议是否有效?我很想知道!
  • 我目前正忙于另一个问题,当我尝试您的建议时肯定会报告。目前我正在使用我在帖子末尾添加的 JNDI 查找解决方法。
  • @TomAnderson 查看我帖子的更新以及我对您的回答的评论。

标签: jsf ejb-3.0


【解决方案1】:

我希望我对 EE 规范的这个领域有足够的了解,可以给你一个明确的答案,但我没有。

JBoss EJB documentation 有话要说:

  • @EJB 注释还有一个 mappedName() 属性。规范将其保留为供应商特定的元数据,但 JBoss 将 mappedName() 识别为您正在引用的 EJB 的全局 JNDI 名称。如果您指定了 mappedName(),则所有其他属性都将被忽略,并且此全局 JNDI 名称用于绑定。
  • 如果您指定 @EJB 且未定义任何属性 [...],则适用以下规则:
    • 在引用 bean 的 EJB jar 中搜索带有接口的 EJB,用于@EJB 注入。如果有多个 EJB 发布相同的业务接口,则会引发异常。如果只有一个具有该接口的 bean,则使用该接口。
    • 在 EAR 中搜索发布该接口的 EJB。如果有重复,则抛出异常。否则返回匹配的 bean。
    • 在 JBoss 中全局搜索该接口的 EJB。同样,如果重复,则会引发异常。
  • @EJB.beanName() 对应于 .如果定义了 beanName(),则使用与 @EJB 相同的算法,除了在搜索中使用 beanName() 作为键之外没有定义任何属性。如果您使用 ejb-link '#' 语法,则此规则有一个例外。 '#' 语法允许您在 EAR 中放置您引用的 EJB 所在的 jar 的相对路径。有关详细信息,请参阅规范

“在 JBoss 中全局搜索该接口的 EJB”当然表明像您编写的那样的注入应该可以工作。事实上,它应该在没有beanName 的情况下工作。但是,我怀疑从 WAR 中的组件的角度来看,EJB-JAR 中的组件是远程的,因此您需要使用远程接口。

所以,我要尝试的第一件事是:

@EJB
private IUserFacadeRemote userFacade;

如果没有beanName,以防出现问题。不过,听起来你已经尝试过了。

如果正常的注入方法不起作用,我可能会转而尝试通过mappedName 进行注入,这在 JBoss 中是一个全局 JNDI 名称。所以:

@EJB(mappedName = "java:global/Users-ejb/userFacade!facades.IUserFacadeRemote")
private IUserFacadeRemote userFacade;

这显然是相当丑陋的。

无论如何,祝你好运!

编辑:您可以尝试的其他方法是使用合格的相对 beanName,它明确命名 EJB-JAR:

@EJB(beanName = "Users-ejb.jar#userFacade")
private IUserFacadeRemote userFacade;

由于 WAR 和 EJB-JAR 未打包在 EAR 中,因此可能需要:

@EJB(beanName = "../Users-ejb.jar#userFacade")
private IUserFacadeRemote userFacade;

但到目前为止,我只是猜测。

编辑反击:我们可能忽略了一些非常简单的事情。 @EJB 注释的 lookup 属性允许您指定“包含目标 EJB 组件的 JNDI 名称的可移植查找字符串”,因此:

@EJB(lookup = "java:global/Users-ejb/userFacade!facades.IUserFacadeRemote")
private IUserFacadeRemote userFacade;

可能会奏效。这本质上是 JBoss 特定使用 mappedName 的可移植版本。

【讨论】:

  • 好的,所以“@EJB”不起作用。带有您提供的 mappedName 属性的“@EJB”确实有效。因此,似乎只剩下一种依赖于供应商的方式来执行此注入或必须使用 JNDI 查找。在不理解为什么其他更简单的注释没有起到作用的情况下使用变通方法有点令人不安。我可能必须把它带到 JBoss 论坛才能查明真相。
  • 编辑了另一个想法。
  • 我不认为对“JBoss EJB 文档”的引用对于 Jboss 7.1 是正确的。我调试了服务器,它只查看当前应用程序内部。它从不全局搜索。
【解决方案2】:

我一直在 Wildfly 中测试这个场景,发现如果 war 内部有一个指向 ejb 的 jboss-deployment-structure.xml,它将与上面描述的本地接口一起工作。否则会抛出 ClassNotFoundException,因为由于 JBoss 和 Wildfly 中的模块化类加载,上述战争无法真正“了解” ejbs 类。文件内容应为:

<jboss-deployment-structure>
    <deployment>
        <dependencies>
            <module name="deployment.Users-ejb.jar" />
        </dependencies>
    </deployment>
</jboss-deployment-structure>

然后JSF bean就可以使用了:

@EJB(lookup = "java:global/Users-ejb/userFacade!facades.IUserFacadeRemote")
private IUserFacadeLocal userFacade;

【讨论】:

    【解决方案3】:

    正如@TomAnderson 所说,实现跨工件查找的标准方法是@EJB 注释的lookup attribute

    这里有一个完整的 Maven 项目来说明它是如何工作的:
    https://github.com/mrts/remote-ejb-injection

    您不需要使用 EJB 类的name 属性,只要在查找中提供类名就足够了。引用上面的例子:

    // in API JAR
    @Remote
    public interface HelloService { ... }
    
    // in EJB JAR
    @Stateless
    public class HelloServiceImpl implements HelloService { ... }
    
    // in WAR
    @WebServlet("/hello")
    public class HelloServlet extends HttpServlet {
    
        @EJB(lookup = "java:global/service-ear/service-ejb-impl/HelloServiceImpl!" +
                      "ee.mrts.service.HelloService")
        private HelloService helloService;
    
        ...
    }
    

    (因此,在 lookup Just Works™ 中直接使用 HelloServiceImpl。)

    【讨论】:

      猜你喜欢
      • 2023-03-03
      • 2017-04-19
      • 2017-04-13
      • 1970-01-01
      • 2014-10-09
      • 2014-09-23
      • 1970-01-01
      • 2012-08-30
      • 1970-01-01
      相关资源
      最近更新 更多