尝试加载时出现 NoClassDefFoundError class

NoClassDefFoundError when trying to load class

我在尝试实例化某些 class

时收到 java.lang.NoClassDefFoundError

我将尝试简化我的项目结构:我有 2 个 jar 文件 A(里面有 a.class)和 B(里面有 b.class)我正在尝试实例化一个 'b' class 里面 'a' 代码。 JAR A 依赖于 JAR BJAR A 是位于 application/lib 中的常规 JAR 文件,JAR B 打包为 EJB_JAR.

我正在使用 glassfishJ2EE 以及 maven 我是 J2EE 的新手,我已经尝试查找它了。我发现这可能是一个 class 加载器问题,因为从 lib ( A) 加载 classes 的类加载器是加载 EARs WAR 和 EJB_JARs 的类加载器的祖先,因此因为我无法从 'a'

加载 class 'b' 的可见性问题

此外,当我尝试在调试器中从位于 Jar-A 中的 classes 调用(使用 "expression evaluator")Class.forName("com.package.SomeClass") 以加载 class JAR-A 我得到一个 class,但是当我尝试加载位于 Jar-B 中的 classes 时,我得到 java.lang.NoClassDefFoundError 异常。

事实是,构造函数中传递的 EJB 正确地包含了所有 EJB 字段,所以我认为它应该可以工作,并且一切都已成功编译。

如何解决这个问题?

最奇怪的事情: 我正在使用驻留在 JAR_A 中的流口水,并且 JAR_A 有一些常规 class 试图调用 b.class (在 JAR_B 中)

从 a.class 调用 b.class 不起作用, 但是直接从规则调用 b.class(从 CommandFactory.newSetGlobal("Bclass",b) 得到 b.class)工作得很好。 怎么可能?

当我将它作为对象从 JAR_B 传递时,它可以正常工作并调用。

回顾

你说:

I am trying to instantiate a 'b' class inside 'a' code. JAR A is dependant on JAR B. JAR A is a regular JAR file which is located in application/lib and JAR B is packaged as an EJB_JAR.

据我了解,您有一个 pom.xml 来构建 jar A,其中声明 jar B 是它的 <dependency/>

然后我看到您的部署场景的两种可能情况:您将 jar 作为 EAR 部署到应用程序服务器,其中 jar A 作为库包含在该 EAR 中,而 jar B 是其中的部署,或者您正在尝试从另一个不相关的应用程序中使用 B。

在任何一种部署情况下,这都是一个错误,但这可能是由于错误地表达了您的依赖关系,或者错误地访问了 EJB。

嵌套部署案例

如果这是一个嵌套部署,其中 jar A 作为一个库包含在 EAR 中,那么您有依赖表达式问题。 EAR 库不能依赖于 EAR 本身,它只能是相反的方式。毕竟,这个 库的定义,对吧? :)

您必须重构您的应用程序以匹配您尝试在此处实现的用例。有关详细信息,请参阅优秀的 Patterns of Modular Architecture RefCard from DZone.

应用客户案例

如果您正在编写的是一个隔离的(甚至可能是独立的)客户端,它将调用 EJB 上的某些操作,您应该做的是创建一个 接口 (本地或远程,取决于您部署客户端的方式)并将其与客户端应用程序和您的 EJB 打包在一起。

然后在您的客户端应用程序中使用 JNDI 查找来获取对远程 EJB 的引用并通过接口使用它:

Context foo = new InitialContext(remoteJndiServiceProperties);
MyBeanInterface bar = (MyBeanInterface)foo.lookup("com.mycompany.MyBeanInterface");
bar.doStuff();

当然,必须正确表达远程 JNDI 注册表属性和您的 bean 的业务接口名称。有关详细信息,请参阅 EJB FAQ for Glassfish

如果您的客户端 运行 在同一个部署单元中,则更简单 - 在这种情况下,您可以只使用 @EJB 注释并注入 no-interface EJB 参考。

有关使用 GlassFish 的独立客户端的详细信息,请参阅 Developing Application Clients with ACC 指南,其中涵盖了所有可能的部署方案。

这背后的一些理论

运行 调试器中的应用程序(或查看当您的客户端调用 EJB 上的方法并将对象作为参数传递给它时获取的堆转储)。

您将看到 EJB 容器(即您的 EJB)并未与您认为的实际 class 一起工作,而是与称为 的静态代理一起工作 class,由容器动态生成。

因此,当您在 EJB 中调用 instanceof 运算符时,检查您正在使用的 class 的类型是否正确,它将计算为 true,但是当您尝试对其进行类型转换时,您将得到 ClassCastException.

这是 EJB 规范所要求的,您对此无能为力,除了传递对象而不是作为引用,而是作为序列化数据(这将花费您)。

反之亦然,因为容器必须能够拦截从外部对 EJB 所做的任何事情,并作出反应(例如未经授权使用限制方法、事务处理等)。

顺便说一句,您上面描述的很多内容都是非法的。 ;)

例如,在 EJB 容器中使用 Class.forName() 手动加载 classes - EJB 容器应该管理对象的生命周期以及使用工厂方法无法获得的任何东西,甚至更好的是,使用 "compatible" 机制,例如 CDI producerdependency injection,应该作为参数传递给您的 EJB。

同样值得怀疑的是您尝试将 EJB 实例传递给容器外的应用程序 运行 的方式。如果您需要访问您的 EJB 以调用它们的方法,您应该通过 EJB 客户端来完成,在您的情况下最有可能通过 远程接口.

此外,如果您仍想继续使用您的方法,请查看 classloader hell 的定义 - 您可能想从 this article 开始,但我想它和其他的一样好。