Резюме:Загрузка банки из работающей программы Java вызывает NoClassDefFoundError
вызванной ClassNotFoundException
, вызванной зависимостями между классом (например import
заявлений). Как я могу обойти это?Загрузка банки во время выполнения вызывает NoClassDefFoundError/ClassNotFoundException
Проблема более подробно:
Я пытаюсь программно загрузить файл банку - давайте назовем его «Сервер» - в виртуальной машине Java через мою собственную программу Java - давайте вызов это «ServerAPI» - и использовать расширение и некоторые другие трюки для изменения поведения и взаимодействия с Сервером. ServerAPI зависит от сервера, но если сервер отсутствует, ServerAPI по-прежнему должен иметь возможность запускать и загружать сервер с веб-сайта.
Чтобы избежать ошибок, вызванных загрузкой ServerAPI без удовлетворения его зависимостей от Сервера, я создал программу запуска - назовем ее «Launcher» - это значит, что необходимо загрузить Сервер и настроить ServerAPI по необходимости, а затем загрузить Сервер и ServerAPI, затем запустите ServerAPI.
Однако, когда я пытаюсь загрузить баночки из Launcher, я получаю ошибки, вызванные тем, что ClassLoaders не могут разрешить другие классы в файле, от которого зависит его класс. Короче говоря, если я попытаюсь загрузить класс A
, он выдаст ошибку, если A
импортирует B
, потому что я еще не загрузил B
. Однако, если B
также импортирует A
, я застреваю, потому что не могу понять, как загрузить сразу два класса или как загрузить класс без проверки JVM.
Почему все ограничения привели меня к этой проблеме:
Я пытающийся изменить и добавить к поведению сервера, но для сложных юридических причин, я не могу изменить программу непосредственно, так что я созданный ServerAPI, который зависит и может настроить поведение Сервера извне.
Однако, по более сложным юридическим причинам, Server и ServerAPI не могут быть просто загружены вместе. Launcher (см. Выше) должен быть загружен с ServerAPI, тогда Launcher необходимо загрузить Server. Наконец, ServerAPI может быть запущен с использованием Server в качестве зависимости. Вот почему эта проблема настолько сложна.
Эта проблема также применима к более поздней части проекта, которая будет включать интерфейс API на основе плагинов, который должен иметь возможность загружать и выгружать плагины из файлов jar во время работы.
исследований я уже сделал по этой проблеме:
Я прочитал и не быть оказана помощь:
- this question, который только рассматривается вопрос о единой методике и не адресные ошибки межклассовой зависимости;
- this question, который не будет работать, потому что я не могу выключать и перезапускать программу каждый раз, когда ящик загружается или выгружается (в основном для части плагина, о которой я кратко упомянул);
- this question, который работает только в ситуациях, когда зависимости присутствуют при запуске программы;
- this question, который имеет такую же проблему, как # 2;
- this question, что имеет ту же проблему, что и # 3;
- this article, из которого я узнал о скрытом loadClass (String, Boolean) метод, но пытаюсь с истинных и ложных значений не помог;
- this question, который имеет ту же проблему, что и # 1;
и больше. Ничего не сработало.
// EDIT: попытки я сделал до сих пор:
Я попытался с помощью URLClassLoaders, чтобы загрузить банку с помощью JarEntries
из JarFile
подобной this question. Я пробовал это как с помощью метода, так и с помощью класса, который расширяет 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
// END EDIT
// EDIT 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;
}
// END EDIT 2
Я понимаю, что там должно быть простое решение этой проблемы, но для жизни меня я не могу найти его. Любая помощь заставила бы меня вечно благодарить. Спасибо.
Вы подтвердили из трассировки стека, что это фактически сам импорт, вызывающий проблему, а не (скажем) непреднамеренный доступ другого класса из конструктора или статической переменной? (И если это действительно импорт, что произойдет, если вы удалите импорт и полностью квалифицируете все ссылки на объекты для класса?) Наконец, можете ли вы опубликовать, как вы загружаете банку? –
В таких случаях может быть полезно продумать очень простой пример с двумя классами без ничего в отдельных банках и выяснить, есть ли у них такая же проблема при их загрузке. Если нет, продолжайте увеличивать сложность модели, пока она не отразится на шаблонах, используемых в вашем реальном приложении. Например, я заметил (в среде OSGi), что загрузчик классов любит разрешать все классы, которые так или иначе ссылаются на класс, который я загружаю напрямую, но он не пытается разрешить классы, на которые ссылаются в одном -step-remove классы до тех пор, пока они не будут вызваны. –
(В этом случае добавить класс прокладок между исходным и целевым классами было достаточно, чтобы сделать загрузчик классов счастливым, если класс прокладок никогда не запускается до загрузки целевого класса.) –