摘要:从正在运行的Java程序加载jar文件会导致
为了避免ServerAPI在没有满足其对Server的依赖关系的情况下加载而导致的错误,我创建了一个启动器(称为“Launcher”),旨在下载Server并根据需要设置ServerAPI,然后加载Server和ServerAPI,最后运行ServerAPI。
然而,当我尝试从Launcher加载jar文件时,我会遇到错误,因为ClassLoader无法解析它正在加载的类所依赖的文件中的其他类。简而言之,如果我尝试加载类
但是,由于更复杂的法律原因,Server和ServerAPI不能简单地一起下载。必须下载Launcher(见上文)与ServerAPI一起下载,然后Launcher需要下载Server。最后,ServerAPI可以使用Server作为依赖项运行。这就是为什么这个问题如此复杂。
这个问题也将适用于项目的后续部分,该部分涉及基于插件的API接口,需要能够在运行时从jar文件中加载和卸载插件。 我已经对这个问题进行了研究: 我已经阅读并未得到帮助:
NoClassDefFoundError
错误,这是由于类之间的依赖关系(例如import
语句)导致的ClassNotFoundException
。我该怎么解决它?
更详细的问题描述:
我正试图通过自己的Java程序(称为“ServerAPI”)以编程方式将一个名为“Server”的jar文件加载到Java虚拟机中,并使用扩展和其他技巧修改并与Server交互。ServerAPI依赖于Server,但如果Server不存在,ServerAPI仍然必须能够运行并从网站下载Server。为了避免ServerAPI在没有满足其对Server的依赖关系的情况下加载而导致的错误,我创建了一个启动器(称为“Launcher”),旨在下载Server并根据需要设置ServerAPI,然后加载Server和ServerAPI,最后运行ServerAPI。
然而,当我尝试从Launcher加载jar文件时,我会遇到错误,因为ClassLoader无法解析它正在加载的类所依赖的文件中的其他类。简而言之,如果我尝试加载类
A
,则如果A
导入B
,它将引发错误,因为我还没有加载B
。然而,如果B
也导入A
,我就卡住了,因为我无法弄清如何同时加载两个类或如何加载一个类而不运行其验证。
为什么所有限制都导致了这个问题:
我试图修改和添加Server的行为,但由于复杂的法律原因,我不能直接修改该程序,因此我创建了依赖于Server并可以从外部调整Server行为的ServerAPI。但是,由于更复杂的法律原因,Server和ServerAPI不能简单地一起下载。必须下载Launcher(见上文)与ServerAPI一起下载,然后Launcher需要下载Server。最后,ServerAPI可以使用Server作为依赖项运行。这就是为什么这个问题如此复杂。
这个问题也将适用于项目的后续部分,该部分涉及基于插件的API接口,需要能够在运行时从jar文件中加载和卸载插件。 我已经对这个问题进行了研究: 我已经阅读并未得到帮助:
- 这个问题只解决了单个方法的问题,没有解决类之间依赖错误的问题;
- 这个问题无法解决我的问题,因为我不能每次加载或卸载jar时都关闭并重新启动程序(主要是对插件部分做了简短的提及);
- 这个问题只适用于程序启动时存在依赖关系的情况;
- 这个问题和第2个问题一样;
- 这个问题和第3个问题一样;
- 这篇文章让我了解了隐藏的loadClass(String, boolean)方法,但使用true和false值尝试后并没有帮助;
- 这个问题和第1个问题一样;
还有更多的尝试,但都没有成功。
//编辑: 我已经尝试过以下方法:
我尝试使用URLClassLoader来加载jar文件,使用JarFile
中的JarEntries
,类似于这个问题。我尝试了使用URLClassLoader
的loadClass(String)
方法和创建一个扩展了URLClassLoader
的类,以便我可以利用loadClass(String, boolean resolve)
来强制ClassLoader
解析它加载的所有类。无论哪种方式,我都得到了同样的错误:
I couldn't find the class in the JarEntry!
entry name="org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapAttributeConverter.class"
class name="org.apache.logging.log4j.core.appender.db.jpa.converter.ContextMapAttributeConverter"
java.lang.NoClassDefFoundError: javax/persistence/AttributeConverter
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:455)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:367)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at Corundum.launcher.CorundumClassLoader.load(CorundumClassLoader.java:52)
at Corundum.launcher.CorundumLauncher.main(CorundumLauncher.java:47)
Caused by: java.lang.ClassNotFoundException: javax.persistence.AttributeConverter
at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 12 more
//结束编辑
//编辑2:
这是我用来在尝试解析类时加载类的代码示例。 这是我创建的一个扩展URLClassLoader
的类中的代码。 在以Class<?> clazz = loadClass(
开始的行上,我尝试使用true和false作为布尔参数; 两种尝试都导致了上述相同的错误。
public boolean load(ClassLoadAction class_action, FinishLoadAction end_action) {
// establish the jar associated with this ClassLoader as a JarFile
JarFile jar;
try {
jar = new JarFile(jar_path);
} catch (IOException exception) {
System.out.println("There was a problem loading the " + jar_path + "!");
exception.printStackTrace();
return false;
}
// load each class in the JarFile through its JarEntries
Enumeration<JarEntry> entries = jar.entries();
if (entries.hasMoreElements())
for (JarEntry entry = entries.nextElement(); entries.hasMoreElements(); entry = entries.nextElement())
if (!entry.isDirectory() && entry.getName().endsWith(".class"))
try {
/* this "true" in the line below is the whole reason this class is necessary; it makes the URLClassLoader this class extends "resolve" the class,
* meaning it also loads all the classes this class refers to */
Class<?> clazz = loadClass(entry.getName().substring(0, entry.getName().length() - 6).replaceAll("/", "."), true);
class_action.onClassLoad(this, jar, clazz, end_action);
} catch (ClassNotFoundException | NoClassDefFoundError exception) {
try {
close();
} catch (IOException exception2) {
System.out.println("There was a problem closing the URLClassLoader after the following " + exception2.getClass().getSimpleName() + "!");
exception.printStackTrace();
}
try {
jar.close();
} catch (IOException exception2) {
System.out.println("There was a problem closing the JarFile after the following ClassNotFoundException!");
exception.printStackTrace();
}
System.out.println("I couldn't find the class in the JarEntry!\nentry name=\"" + entry.getName() + "\"\nclass name=\""
+ entry.getName().substring(0, entry.getName().length() - 6).replaceAll("/", ".") + "\"");
exception.printStackTrace();
return false;
}
// once all the classes are loaded, close the ClassLoader and run the plugin's main class(es) load() method(s)
try {
jar.close();
} catch (IOException exception) {
System.out.println("I couldn't close the URLClassLoader used to load this jar file!\njar file=\"" + jar.getName() + "\"");
exception.printStackTrace();
return false;
}
end_action.onFinishLoad(this, null, class_action);
System.out.println("loaded " + jar_path);
// TODO TEST
try {
close();
} catch (IOException exception) {
System.out.println("I couldn't close the URLClassLoader used to load this jar file!\njar file=\"" + jar_path + "\"");
exception.printStackTrace();
return false;
}
return true;
}
//结束编辑 2
我意识到这个问题肯定有一个简单的解决方案,但是我却找不到。任何帮助都将让我感激不尽。谢谢。
loadClass(String, boolean)
;但由于同样的原因,两者都没有起作用。我将在问题中添加有关我的加载尝试的更多信息,包括堆栈跟踪。 - Variadicism