摘要:从正在运行的Java程序加载jar会导致由类间依赖(例如导入
语句)引起的ClassNotFoundExctive
引起的NoClassDefFoundError
。我如何绕过它?
更详细的问题是:
我试图通过自己的Java程序将jar文件(我们称之为“服务器”)以编程方式加载到Java虚拟机中(我们称之为“ServerAPI”),并使用扩展和其他一些技巧来修改服务器的行为并与之交互。ServerAPI依赖于服务器,但如果服务器不存在,ServerAPI仍然必须能够从网站上运行和下载服务器。
为了避免由于ServerAPI加载而不满足其对服务器的依赖而导致的错误,我制作了一个启动器——让我们称之为“启动器”——它旨在下载服务器并根据需要设置ServerAPI,然后加载服务器和ServerAPI,然后运行ServerAPI。
然而,当我试图从Launcher加载JAR时,会出现错误,因为类加载器无法解析它所加载的类所依赖的文件中的其他类。简而言之,如果我试图加载类A
,如果A
导入B
,它将抛出一个错误,因为我还没有加载B
。然而,如果B
也导入了A
,我就卡住了,因为我不知道如何一次加载两个类,或者如何在没有JVM运行其验证的情况下加载一个类。
为什么所有的限制都让我遇到了这个问题:
我试图修改和添加服务器的行为,但由于复杂的法律原因,我无法直接修改程序,因此我创建了ServerAPI,它依赖于并可以从外部调整服务器的行为。
然而,由于更复杂的法律原因,服务器和服务器API不能简单地一起下载。Launcher(见上文)必须使用ServerAPI下载,然后Launcher需要下载服务器。最后,可以使用服务器作为依赖项来运行ServerAPI。这就是为什么这个问题如此复杂。
这个问题也将适用于项目的后续部分,这将涉及一个基于插件的API接口,该接口需要能够在运行时从jar文件加载和卸载插件。
我已经对这个问题进行了研究:
我通读了一遍,但没有得到以下方面的帮助:
等等。什么都没用。
//编辑:我迄今为止的尝试:
我已经尝试过使用URLClassLoaders从JarFile
中使用JarEntries
加载jar,类似于这个问题。我使用并调用了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
。在以类开头的行中
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
我意识到必须有一个简单的解决办法,但就我的一生而言,我似乎找不到它。任何帮助都会让我永远感激。非常感谢。
令人尴尬的是,我发现答案是错误消息说的是实话。javax.persistence.属性转换器
,加载程序声称的类不存在,不在jar中。
我修复了这个问题,只加载了主类和ClassLoader
所有引用类,基本上加载了jar中程序中使用的所有类,这就是我所需要的。
现在,我可以发誓,我之前检查过这个,发现了那个班级;我想当我检查时,我一定是检查了类的Apache开源存储库,而不是实际的服务器。我不记得了。
在任何情况下,AttributeConverter
都会丢失。我不知道他们是如何或为什么设法编译一个缺少依赖项的jar的,但我猜他们的主要进程从来没有使用过代码的这一部分,所以它从来没有抛出错误。
很抱歉浪费了大家的时间。。。包括我自己。这个问题我已经纠结了一段时间了。
这个故事的寓意:
如果你试图加载一个可执行的jar,除非你真的必须加载,否则不要在jar中加载所有的类。只需加载主类;这将加载程序需要运行的所有内容。
//编辑:
我现在开始出现同样的错误,但直到我尝试从加载的类中调用一个方法时才会出现。这个问题显然仍然悬而未决。请投反对票,不要理会这个答案。
附上一个拉链,里面有三个文件夹。Shmer示例,ShimimDriver和ShimimroidInstrumentDriver。Shimime示例是主程序,并依赖于其他两个项目作为库。 我启动AndroidStudio 1.1并将“ShimmerExample”作为eclipse项目导入。导入摘要。txt在下面。 该项目构建良好,并加载到android设备上。启动应用程序后,我得到该类位于vecm
问题内容: 我试图在运行时将jar文件添加到classpath。我用这个代码 系统输出打印此URL: 我正在仔细检查此路径,此jar存在。即使这个测试也显示com.mysql.jdbc。驱动程序类存在。 但是,当我使用此Class.forName(driver)时,仍然会收到java.lang.ClassNotFoundException。此代码有什么问题? 问题答案: URL可以,但是您仍然尝试
现在,当我运行和时,一切都很好,但是当我尝试运行项目时(在build/libs/中运行构建的jar和在IntelliJ中运行velotest类时也是如此),我会得到以下错误: 线程“main”java.lang.noClassDefFounderror:org/apache/commons/collections/extendedProperties 在org.apache.velocity.ru
我见过很多人对在Eclipse中运行时加载的图像有相同的问题,但在从导出的JAR中运行时没有。我一直有这个问题,直到我找到了正确的解决方案。现在我遇到了一个问题,当从一个新导出的、可运行的JAR运行时,图像被加载,程序工作得非常完美,但在Eclipse中却不行!值得注意的是,我在我的机器上更新了Java! 它在Eclipse中运行得非常好,直到我解决了如何正确导出可运行的JAR,现在却没有!资产文
问题内容: 我被要求构建一个Java系统,该系统在运行时能够加载新代码(扩展)。我的代码运行时如何重新加载jar文件?或如何加载新的jar? 显然,由于持续的正常运行时间很重要,因此,我想增加在此过程中重新加载现有类的功能(如果这样做不会使事情复杂化)。 我应该注意什么?(将其视为两个不同的问题-一个关于在运行时重新加载类,另一个关于添加新类)。 问题答案: 用现有数据重新加载现有类可能会破坏事情
我使用以下spock依赖项 我写了这个测试 测试成功 现在我想使用Spring在规范中注入用注释,并在运行应用程序时成功注入。测试和组件都放在同一个maven模块中。 我在这里阅读了关于:如何将Spring bean注入spock测试 因此我添加了这个依赖版本 在添加了运行s的依赖后 然后我尝试使用适当的注释 结果仍然是上述例外。 groovy测试在下,这是一个可以在第一次成功运行测试时看到的工作