Java 加载依赖于内部类的外部类

Java 加载依赖于内部类的外部类,java,plugins,reflection,classloader,Java,Plugins,Reflection,Classloader,我正在尝试为Java编写一个简单的插件体系结构(它扩展了我们现有的一些功能,这就是为什么我没有使用合适的插件库) 我的war中有一个基类(使用JBoss部署): 我有一个jar自定义组件。jar包含一些自定义组件代码: package org.example.components.custom; public class CustomComponent extends ComponentBase { // Custom code } 通过一些魔法,我的war中的插件代码获得了Cust

我正在尝试为Java编写一个简单的插件体系结构(它扩展了我们现有的一些功能,这就是为什么我没有使用合适的插件库)

我的war中有一个基类(使用JBoss部署):


我有一个jar
自定义组件。jar
包含一些自定义组件代码:

package org.example.components.custom;

public class CustomComponent extends ComponentBase {
    // Custom code
}
通过一些魔法,我的war中的插件代码获得了
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/…中),你需要从类路径上的
某处删除
自定义组件.jar
,并将其移动到WAR文件的“WEB-INF/lib”文件夹中

这样,两个类都由同一个类加载器加载

现在的方式是,当您执行以下操作时:

Class.forName('org.example.components.ComponentBase')
它之所以成功,是因为您正在将ComponentBase加载到war类加载器中。forName方法首先检查war的WEB-INF/lib和WEB-INF/class并找到它

然而,当你执行

Class.forName("org.example.components.custom.CustomComponent");
war类加载器首先像以前一样检查WEB-INF/lib、WEB-INF/classes,但在那里找不到它。然后它开始询问“父”类加载器。父类加载器(可能是JBoss特定的类加载器,或者引导类加载器?)找到类并尝试加载它

一切进展顺利,直到它检测到CustomComponent扩展了BaseComponent。因此父类加载器尝试加载BaseComponent。但是,父类加载器找不到BaseComponent,因为不允许它在war文件中查找。war类加载器是一个子类加载器,类加载器只能委托给父类加载器

考虑一下,如果你取消了战争部署,你的基础阶级也离开了,会发生什么。或者如果你部署了两场战争,都是用你的基类。引导类路径上的插件类依赖于可能消失或倍增的基类的想法毫无意义


现在,只需将所有代码放在WAR文件中,并使类加载逻辑尽可能简单。

谁是“it”?你能跟踪堆栈吗?还有:
Class.forName
抛出异常。我编辑了这篇文章以使其更加清晰。
ServiceLoader
类在这个场景中不会有太大的用处,因为找到
CustomComponent
类名的“魔法”是基于bean配置和注释的混合,这些配置和注释与现有的内部约定相匹配。没有固定的基类集要求
ServiceLoader
查找。custom-components.jar在哪里?在war WEB-INF/lib中?
自定义组件。jar
就在类路径上的某个地方。理论上,如果我们知道jar文件的路径,我们可以创建一个新的
URLClassLoader
,它首先从jar文件加载,然后将JBoss类加载器作为父类
CustomComponent
应该在
URLClassLoader
中找到,然后当它查找
BaseComponent
时,它将查找父JBoss类加载器。然而,当我尝试它时,我仍然得到相同的错误。这与您描述的模型有什么关系?子级在试图自己解析它之前是否先查看其父级缓存?如果jar不在类路径上,则上述技术有效。这表明它在试图找到它自己之前确实在它的父缓存中查找。
Class.forName("org.example.components.custom.CustomComponent");