我有一个EJB 3模块部署在我的Glassfish 2.1服务器上。
我正在尝试部署第二个EJB模块,它依赖于第一个模块,但是部署失败java.lang.NoClassDefFoundError关于一个可以在第一个EJB模块中找到的类。
解决两个EJB模块之间的依赖关系的最佳方法是什么?我想分别部署它们,而不是将它们放在同一个EAR中。
更具体地说,我在第二个EJB模块的一个EJB中有来自第一个EJB模块的依赖项注入:
@EJB (name="ejb/FirstEJB")
private FirstEJBRemote ejb;
但在部署期间,我得到NoClassDefFoundError关于类FirstEJBRemote:
Error in annotation processing: java.lang.NoClassDefFoundError: FirstEJBRemote
要理解为什么会出现这个异常,需要掌握两件事:
- 一个EJB如何引用另一个EJB 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_B在编译时使用FirstEJBRemote
:
private FirstEJBRemote ejb;
要在运行时向EJB提供这个外部类/库,必须执行以下操作:
- 将类/库放入应用服务器的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包含某个库的version1(例如,日志库),并且EJB_B使用相同的库,但是version2时会发生什么。这两个类都会被加载,你会在任何地方遇到冲突,ClassCastException
等(或者如果你幸运的话,EJB_B将"只"被迫使用version1,因为这个类已经被classloader加载了)。
最后,摘自Oracle GlassFish 3.1指南:
绕过类装入器隔离
由于每个应用程序或单独部署的模块类加载器Universe是隔离的,应用程序或模块不能加载类从另一个应用程序或模块。这可以防止两个名称相似的类在不同的应用程序或模块中的干扰对方。
Miljen Mikic对整个classloader加载常用命名的类有一个观点,如果它们不被隔离的话。
但是对于Glassfish,您应该做的是将您的第一个EJB模块放在目录glassfish_home/domains/domain1/lib/applibs中。然后,在部署第二个EJB模块期间,您可以指定应该为正在部署的这个EJB模块加载第一个EJB模块的jar。
这被称为特定于应用程序的类加载,这里有更多关于它的内容:http://docs.oracle.com/cd/E19798-01/821-1752/gatej/index.html
试试这个
@ ejb(查找="JNDI_BEAN_NAME")