[英]Loading external classes that depend on internal classes
我正在尝试为Java写一个简单的插件架构(它扩展了我们现有的某些功能,这就是为什么我没有使用适当的插件库的原因)
我在战争中有一个基类(使用JBoss部署):
package org.example.components;
public abstract class ComponentBase {
// Base code
}
我有一个jar custom-components.jar
其中包含一些自定义组件代码:
package org.example.components.custom;
public class CustomComponent extends ComponentBase {
// Custom code
}
通过一些魔术,我的战争中的插件代码获得了CustomComponent
类的名称并执行以下操作:
Class classToRegister = Class.forName("org.example.components.custom.CustomComponent");
但是,这引发了NoClassDefFoundError
,表示找不到org.example.components.ComponentBase
。
Caused by: java.lang.NoClassDefFoundError: org/example/components/ComponentBase
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at org.jboss.mx.loading.RepositoryClassLoader.findClassLocally(RepositoryClassLoader.java:690)
at org.jboss.mx.loading.RepositoryClassLoader.findClass(RepositoryClassLoader.java:670)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at org.jboss.mx.loading.RepositoryClassLoader.loadClassLocally(RepositoryClassLoader.java:200)
at org.jboss.mx.loading.ClassLoadingTask$ThreadTask.run(ClassLoadingTask.java:131)
at org.jboss.mx.loading.LoadMgr3.nextTask(LoadMgr3.java:399)
at org.jboss.mx.loading.RepositoryClassLoader.loadClassImpl(RepositoryClassLoader.java:527)
at org.jboss.mx.loading.RepositoryClassLoader.loadClass(RepositoryClassLoader.java:415)
at java.lang.ClassLoader.loadClass(ClassLoader.java:295)
at java.net.FactoryURLClassLoader.loadClass(URLClassLoader.java:627)
at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1345)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1204)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:169)
... 259 more
Caused by: java.lang.ClassNotFoundException: No ClassLoaders found for: org/example/components/ComponentBase
at org.jboss.mx.loading.LoadMgr3.beginLoadTask(LoadMgr3.java:212)
at org.jboss.mx.loading.RepositoryClassLoader.loadClassImpl(RepositoryClassLoader.java:521)
at org.jboss.mx.loading.RepositoryClassLoader.loadClass(RepositoryClassLoader.java:415)
at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
... 297 more
ComponentBase
已加载良好,因为我可以执行Class.forName('org.example.components.ComponentBase')
而不会出现错误。 初始化CustomComponent
时是否使用其他类加载器? 我尝试显式传递类加载器,但出现相同的错误。
由于您的插件基类位于WAR文件中(可能位于WEB-INF / classes / ...中),因此您需要从somewhere on the classpath
删除custom-components.jar
并将其移至“ WEB-INF / lib”中您的war文件的文件夹。
这样,两个类都由同一个类加载器加载。
执行时现在拥有的方式:
Class.forName('org.example.components.ComponentBase')
它成功,因为您正在将ComponentBase加载到战争类加载器中。 forName方法首先检查战争的WEB-INF / lib和WEB-INF / classs并找到它。
但是,当您执行
Class.forName("org.example.components.custom.CustomComponent");
战争类加载器首先像以前一样检查WEB-INF / lib,WEB-INF / class,但在那里找不到它。 然后,它开始询问“父”类加载器。 父类加载器(可能是JBoss特定的类加载器,还是引导类加载器?)找到该类并尝试加载它。
一切正常,直到它检测到CustomComponent扩展了BaseComponent。 因此,父类加载器尝试加载BaseComponent。 但是,父类加载器找不到BaseComponent,因为不允许在war文件中查找它。 您战争类加载器是子类加载器,并且类加载器只能委托给父类加载器。
考虑一下如果您取消部署战争而您的基础阶级走了会发生什么。 或者,如果您部署了两次战争,都与您的基类一起进行。 在启动类路径上依赖于可能消失或成倍增加的基类的插件类的想法是没有意义的。
现在,只需将所有代码放在WAR文件中,并使类加载逻辑尽可能简单即可。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.