Var*_*ism 5 java dependencies runtime jar classloader
简介: 从正在运行的Java程序加载jar会NoClassDefFoundError导致由ClassNotFoundException类间依赖性(例如import语句)引起的.我怎么能绕过它呢?
问题更详细:
我试图以编程方式加载一个jar文件 - 让我们称之为"服务器" - 通过我自己的Java程序进入Java虚拟机 - 让我们称之为"ServerAPI" - 并使用扩展和一些其他技巧来修改行为和服务器交互.ServerAPI依赖于Server,但如果Server不存在,ServerAPI仍然必须能够从网站运行和下载Server.
为了避免因ServerAPI加载而导致的错误而不满足Server的依赖性,我制作了一个启动器 - 让我们称之为"Launcher" - 这是为了下载Server并根据需要设置ServerAPI,然后加载Server和ServerAPI,然后运行ServerAPI.
但是,当我尝试从Launcher加载jar时,我会收到错误,因为ClassLoaders无法解析文件中其加载的类所依赖的其他类.简而言之,如果我尝试加载Class A,它会在A导入时抛出错误,B因为我还没有加载B.但是,如果B还要导入A,我会陷入困境,因为我无法弄清楚如何一次加载两个类或如何在没有JVM运行其验证的情况下加载类.
为什么所有的限制都让我遇到了这个问题:
我试图修改并添加到Server的行为,但由于复杂的法律原因,我无法直接修改程序,所以我创建了依赖的ServerAPI,可以从外部调整Server的行为.
但是,出于更复杂的法律原因,Server和ServerAPI不能简单地一起下载.Launcher(见上文)必须与ServerAPI一起下载,然后Launcher需要下载Server.最后,可以使用Server作为依赖项运行ServerAPI.这就是为什么这个问题如此复杂的原因.
此问题也适用于项目的后续部分,该部分将涉及基于插件的API接口,该接口需要能够在运行时从jar文件加载和卸载插件.
关于这个问题的研究我已经做过:
我已经阅读并且未能得到以下方面的帮助:
和更多.没有任何效果.
//编辑: 我到目前为止所做的尝试:
我一直在使用URLClassLoaders加载使用的jar试图JarEntries从JarFile类似这样的问题.我既使用和调用试过这种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
Run Code Online (Sandbox Code Playgroud)
//结束编辑
//编辑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;
}
Run Code Online (Sandbox Code Playgroud)
//结束编辑2
我意识到必须有一个简单的解决方案,但对于我的生活,我似乎无法找到它.任何帮助都会让我永远感激不尽.谢谢.
尴尬的是,我发现答案是错误消息说的是实话。javax.persistence.AttributeConverter,加载程序声称的类不存在,不在 jar 中。
我通过仅加载主类和ClassLoader所有引用类解决了这个问题,基本上加载了程序中使用的 jar 中的所有类,这就是我所需要的。
现在,我可以发誓我之前检查过这个并找到了那个类;我认为当我检查该类时,我实际上一定是在 Apache 开源存储库中检查了该类,而不是实际的服务器。我不记得了。
无论如何,AttributeConverter失踪了。我不知道他们如何或为何设法编译一个缺少依赖项的 jar,但我猜他们的主进程从不使用这部分代码,因此它从未抛出错误。
我很抱歉浪费了大家的时间……包括我自己的时间。我已经被这个问题困扰了一段时间了。
这个故事的寓意是:
如果您尝试加载可执行 jar,除非确实需要,否则不要费心加载 jar 中的所有类。只需加载主类即可;这将加载程序运行所需的所有内容。
//编辑:
我现在开始遇到同样的错误,但直到我尝试从加载的类调用方法时才会出现该错误。这个问题显然仍然悬而未决。请投反对票并忽略此答案。
| 归档时间: |
|
| 查看次数: |
2704 次 |
| 最近记录: |