尝试加载类时NoClassDefFoundError

时间:2016-01-26 14:44:30

标签: java maven java-ee jar drools

我在尝试实例化某个类

时得到java.lang.NoClassDefFoundError

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

我正在使用glassfishJ2EEmaven我是J2EE的新手,我试图查找一下。我已经发现它可能是一个类加载器问题,因为从lib(A)加载类的类加载器是加载EARs WAR和EJB_JAR的类加载器的祖先因此因为可见性问题我不能从'a加载类'b' “

另外,当我尝试从位于Jar-A中的类调试器中调用(使用“表达式求值程序”)Class.forName("com.package.SomeClass")来加载JAR-A中的类时,我得到一个类,但是当我尝试加载位于Jar-B中的类我得到java.lang.NoClassDefFoundError例外。

问题是,构造函数中传递的EJB正确地包含了所有EJB字段,所以我认为它应该可行,并且所有内容都已成功编译。

如何解决这个问题?

最奇怪的事情: 我正在使用驻留在JAR_A中的drools,而JAR_A有一些试图调用b.class的常规类(在JAR_B中)

从a.class调用b.class不起作用, 但直接从规则(从CommandFactory.newSetGlobal(“Bclass”,b)获得b.class)调用b.class就可以了。 怎么可能?

当我从JAR_B传递它作为一个对象时,它可以工作并且调用很好。

1 个答案:

答案 0 :(得分:2)

小结

你说:

  

我试图在'a'代码中实例化一个'b'类。 JAR A依赖于JAR B. JAR A是一个常规JAR文件,位于application/lib,JAR B打包为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中的优秀DZone RefCard。

应用程序客户案例

如果您正在编写的是一个孤立的(甚至可能是独立的)客户端,它将调用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注释并注入无接口 EJB引用。

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

这背后的一些理论

在调试器中运行应用程序(或查看客户端在EJB上调用方法时所采用的堆转储,并将其作为参数传递)。

您将看到的是EJB容器(即您的EJB)不能使用您认为的实际类,而是使用名为静态代理类的东西,这是由容器动态生成。

因此,当您在EJB中调用instanceof运算符时,检查您正在使用的类是否具有正确的类型,它将评估为true,但是当您尝试时对它进行类型转换,你会得到一个ClassCastException

这是EJB规范所要求的,除了将对象不作为引用传递,而是作为序列化数据传递(这会花费你的成本)之外,你无法做很多事情。

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

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

例如,在EJB容器中使用Class.forName()手动加载类 - EJB容器应该管理对象的生命周期以及使用工厂方法无法获得的任何内容,甚至更好,使用“兼容”机制应该将CDI producer 依赖注入作为参数传递给EJB。

还有一个问题是您尝试将EJB实例传递给在容器外部运行的应用程序。如果您需要访问EJB来调用它们上的方法,您应该通过EJB客户端来实现它,在您的情况下很可能通过远程接口

另外,如果你仍想继续你的方法,请查看 classloader hell 的定义 - 你可能想要从this article开始,但我想它和其他任何一个一样好