[英]Loading a jar at runtime causes a NoClassDefFoundError/ClassNotFoundException
簡介: 從正在運行的Java程序加載jar會導致由類間依賴性(例如import
語句)引起的ClassNotFoundException
導致的NoClassDefFoundError
。 我怎么能繞過它呢?
問題更詳細:
我試圖以編程方式加載一個jar文件 - 讓我們稱之為“服務器” - 通過我自己的Java程序進入Java虛擬機 - 讓我們稱之為“ServerAPI” - 並使用擴展和一些其他技巧來修改行為和服務器交互。 ServerAPI依賴於Server,但如果Server不存在,ServerAPI仍然必須能夠從網站運行和下載Server。
為了避免因ServerAPI加載而導致的錯誤而不滿足Server的依賴性,我制作了一個啟動器 - 讓我們稱之為“Launcher” - 這是為了下載Server並根據需要設置ServerAPI,然后加載Server和ServerAPI,然后運行ServerAPI。
但是,當我嘗試從Launcher加載jar時,我會收到錯誤,因為ClassLoaders無法解析文件中其加載的類所依賴的其他類。 總之,如果我嘗試加載類A
,它會拋出,如果一個錯誤A
進口B
,因為我沒裝B
呢。 但是,如果B
也導入A
,我就會陷入困境,因為我無法弄清楚如何一次加載兩個類或如何在沒有JVM運行其驗證的情況下加載類。
為什么所有的限制都讓我遇到了這個問題:
我試圖修改並添加到Server的行為,但由於復雜的法律原因,我無法直接修改程序,所以我創建了依賴的ServerAPI,可以從外部調整Server的行為。
但是,出於更復雜的法律原因,Server和ServerAPI不能簡單地一起下載。 Launcher(見上文)必須與ServerAPI一起下載,然后Launcher需要下載Server。 最后,可以使用Server作為依賴項運行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
。 在以Class<?> clazz = loadClass(
開頭的行上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
我意識到必須有一個簡單的解決方案,但對於我的生活,我似乎無法找到它。 任何幫助都會讓我永遠感激不盡。 謝謝。
令人尷尬的是,我發現答案是錯誤信息說實話。 javax.persistence.AttributeConverter
,加載器聲明的類不存在,不在jar中。
我通過僅加載主類和ClassLoader
所有引用類來解決問題,實質上是加載程序中使用的jar中的所有類,這就是我所需要的。
現在,我可以發誓我之前檢查過這個並發現了這個課程; 我想我在檢查時必須實際檢查過該類的Apache開源存儲庫而不是實際的服務器。 我不記得了。
在任何情況下,都缺少AttributeConverter
。 我不知道他們是如何或為什么設法編譯一個缺少依賴項的jar,但我猜他們的主要進程從不使用那部分代碼,因此它從不拋出錯誤。
我很遺憾浪費了每個人的時間......包括我自己的。 我一直堅持這個問題一段時間了。
這個故事的道德:
如果您正在嘗試加載可執行jar,請不要在jar中加載所有類,除非您確實需要。 只需加載主類; 這將加載程序運行所需的一切。
//編輯:
我現在已經開始遇到相同的錯誤,但是直到我嘗試從加載的類中調用方法時它才出現。 問題顯然仍然存在。 請低估並忽略這個答案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.