3

我在 Glassfish 2.1 服务器上部署了一个 EJB 3 模块。

我正在尝试部署第二个 EJB 模块,该模块依赖于第一个模块,但是部署失败,出现 java.lang.NoClassDefFoundError 关于可以在第一个 EJB 模块中找到的类。

解决 2 个 EJB 模块之间的这种依赖关系的最佳方法是什么?我想单独部署它们,而不是将它们放在同一个 EAR 中。

更具体地说,我在我的第二个 EJB 模块的一个 EJB 中从我的第一个 EJB 模块中进行了 EJB 的依赖注入:

@EJB (name="ejb/FirstEJB")
private FirstEJBRemote ejb;

但是在部署过程中,我收到关于 FirstEJBRemote 类的 NoClassDefFoundError:

Error in annotation processing: java.lang.NoClassDefFoundError: FirstEJBRemote
4

3 回答 3

3

要了解为什么会发生此异常,需要掌握两点:

  1. 一个 EJB 如何引用另一个 EJB
  2. EJB 类加载

让我们首先处理第一个,所以让我们命名第一个 EJB(包含FirstEJBRemote类)EJB_A和第二个 EJB(试图访问 EJB_A 的方法)EJB_B。不深入@LocalBean讨论注释和类似的东西,EJB_B 访问 EJB_A 方法的唯一方法是通过接口。换句话说,EJB_A 实现了 EJB_B 声明的某个接口(在您的情况下是FirstEJBRemote),并通过注入检索 EJB_A 的实例。到现在为止还挺好。

接下来,我们必须了解 EJB 类加载器。自然,您的应用程序(在本例中为 EJB)在编译时使用的每个外部类/库也必须在运行时可用。否则,ClassNotFoundException将被抛出,这正是您的情况,因为 EJB_BFirstEJBRemote在编译时使用:

private FirstEJBRemote ejb;

要在运行时向 EJB 提供这个外部类/库,必须执行以下操作:

  • 把class/library放到应用服务器的lib目录下
  • 将类/库与 EJB 打包在一起
  • 将类/库放在包含 EJB 的 EAR 类路径中

我们将忽略第三个选项,因为您说您对 EAR 不感兴趣。因此,您可以将FirstEJBRemote接口(当然是以 .jar 文件的形式)放在 Glassfish 的 lib 目录中,EJB_A 和 EJB_B 都可以使用它(因此,您不必将它与 EJB_A 打包)或者您也可以将此接口与 EJB_B 打包在一起,注意它与 EJB_A 位于同一包中。

在您的评论中,您想知道为什么根本需要这个“复杂”的过程。答案很简单——如果 EJB 类加载器不是相互隔离的,那么使用 EJB_A 部署的每个类都对 EJB_B 可用。在这种特殊情况下,这是希望的行为,但想象一下当 EJB_A 包含某个库(例如日志库)的版本 1 并且 EJB_B 使用相同的库但版本 2 时会发生什么。这两个类都将被加载,您将在任何地方遇到冲突,ClassCastException等等(或者如果幸运的话,EJB_B 将“仅”被迫使用 version1,因为该类已经被类加载器加载)。

最后,摘自Oracle GlassFish 3.1 指南:

规避类加载器隔离

由于每个应用程序或单独部署的模块类加载器世界都是隔离的,因此应用程序或模块无法从另一个应用程序或模块加载类。这可以防止不同应用程序或模块中的两个类似命名的类相互干扰。

于 2013-05-11T13:15:50.620 回答
2

Miljen Mikic 有一个观点,即如果它们不被隔离,整个类加载器会加载通常命名的类。

但是对于 Glassfish,您应该做的是将您的第一个 EJB 模块放在目录glassfish_home/domains/domain1/lib/applibs中。然后在部署第二个 EJB 模块期间,您可以指定应该为正在部署的这个 EJB 模块加载第一个 EJB 模块的 jar。

这称为特定于应用程序的类加载,这里有更多关于它的信息:http: //docs.oracle.com/cd/E19798-01/821-1752/gatej/index.html

于 2013-05-12T18:14:36.367 回答
0

试试这个

@EJB(查找 =“JNDI_BEAN_NAME”)

于 2013-05-10T07:10:58.400 回答