В Java, почему Классы исключений должны быть доступны classloader, прежде чем они будут необходимы?

Я разрабатываю приложение, которое динамично загружает JAR, который содержит определение набора классов, которые это использует. Все пошло прекрасное, пока я не пытался поймать производный класс Исключения, это находится в динамично загруженном JAR.

Следующий отрывок показывает проблему (DynamicJarLoader класс, который на самом деле загружает JAR; оба TestClass и MyException находятся во внешнем JAR):

public static void main(String[] args) {
    DynamicJarLoader.loadFile("../DynamicTestJar.jar");
    try {
         String foo = new TestClass().testMethod("42");
    } catch(MyException e) { }
}

Когда я пытаюсь выполнить его, я получаю это:

Exception in thread "main" java.lang.NoClassDefFoundError: dynamictestjar/MyException
Caused by: java.lang.ClassNotFoundException: dynamictestjar.MyException
        at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
Could not find the main class: dynamicjartestapp.Main. Program will exit.

Если я заменяю catch(MyException e) с catch(Exception e), прекрасные прогоны программы. Это означает, что Java в состоянии найти TestClass после того, как JAR был уже загружен. Таким образом, кажется, что JVM нужны все Классы исключений, которые будут определены, когда программа начинает работать, и не, когда они необходимы (т.е. когда тот конкретный блок try-catch достигнут).

Почему это происходит?

Править

Я запустил некоторые дополнительные тесты, и это действительно довольно нечетно. Это - полный источник MyException:

package dynamictestjar;
public class MyException extends RuntimeException {
    public void test() {
        System.out.println("abc");
    }
}

Этот код выполнения:

public static void main(String[] args) {
    DynamicJarLoader.loadFile("../DynamicTestJar.jar");
    String foo = new TestClass().testMethod("42");
    new MyException().test(); //prints "abc"
}

Это не делает:

  public static void main(String[] args) {
    DynamicJarLoader.loadFile("../DynamicTestJar.jar");
    String foo = new TestClass().testMethod("42");
    new MyException().printStackTrace(); // throws NoClassDefFoundError
  }

Я должен указать, что каждый раз, когда я запускаю свои тесты от NetBeans, все идет согласно плану. Странность начинается только, когда я сильно удаляю внешнюю Банку из глаз Java и запускаю тестовое приложение из командной строки.

РЕДАКТИРОВАНИЕ № 2

На основе ответов я записал это, которое я думаю, доказывает тот, который я принял, является действительно правильным:

  public static void main(String[] args) {
    DynamicJarLoader.loadFile("../DynamicTestJar.jar");
    String foo = new TestClass().testMethod("42");
    class tempClass {
        public void test() {
            new MyException().printStackTrace();
        }
    }
    new tempClass().test(); // prints the stack trace, as expected
  }
9
задан Pedro d'Aquino 21 August 2009 в 17:17
поделиться

4 ответа

Вы загружаете JAR DynamicTestJar.jar динамически во время выполнения, но вы добавляете его в путь к классам при компиляции кода.

Итак, когда загрузчик классов по умолчанию пытается загрузить байт-код для main () , он не может найти MyException в пути к классам и вызывает исключение. Это происходит до `DynamicJarLoader.loadFile (" ../ DynamicTestJar.jar ");` выполняется!

Поэтому вы должны убедиться, что классы из вашего динамического JAR доступны в текущий активный загрузчик классов , когда загружается класс, которому они нужны. Вы можете добавить JAR в путь к классам впоследствии, особенно в классе, который что-то из него импортирует.

1
ответ дан 4 December 2019 в 21:10
поделиться

«Похоже, что JVM требует, чтобы все классы исключений определялись при запуске программы, а не тогда, когда они нужны» - нет, я не думаю, что это правильно.

Держу пари, что ваша проблема связана с этими потенциальными ошибками с вашей стороны, ни одна из которых не имеет ничего общего с JVM:

  1. Вам не хватает инструкции пакета в верхней части файла MyException.java . Похоже, вы имели в виду, что это будет «package dynamictestjar», но его там нет.
  2. У вас был правильный оператор пакета в вашем файле MyException.java, но при компиляции вы не получили MyException.class файл в папке с именем "dynamictestjar".
  3. Когда вы упаковали свой код в DynamicTestJar.jar (вы фанат Звездных войн), вы этого не сделали. Не правильно указать путь, потому что вы не заархивировали каталог, содержащий папку «dynamictestjar», поэтому путь, который видит загрузчик классов, неверен.

Ничто из этого не связано с большими загадками, связанными с загрузчиком классов. Вам нужно проверить и убедиться, что вы делаете свои вещи правильно.

Почему вам нужно динамически загружать JAR-файл? Что вы делаете, чего нельзя было бы сделать, просто поместив JAR в CLASSPATH и позволив загрузчику классов забрать его?

Я надеюсь, что это всего лишь пример, а не показательный для ваших "лучших практик" в Java:

catch(MyException e) { }

Вы должны хотя бы распечатать трассировку стека или использовать Log4J для регистрации исключения.

ОБНОВЛЕНИЕ:

Я должен указать, что всякий раз, когда я запускаю свои тесты из NetBeans, все идет по плану. Странность начинается только тогда, когда я принудительно удаляю внешний JAR из глаз Java и запускаю тестовое приложение из командной строки.

Это говорит о том, что путь, который вы жестко прописали в пути к JAR, не устраивает, когда вы запускать из командной строки.

JVM не работает одним способом с NetBeans, а другим - без него. Все дело в понимании CLASSPATH, загрузчика классов и проблем с путями. Когда разберетесь со своим, все заработает.

6
ответ дан 4 December 2019 в 21:10
поделиться

Является ли MyException классом исключения в JAR, который вы загружаете динамически?

Обратите внимание, что вы статически используете класс MyException, поскольку он есть буквально в вашем коде .

Вы помещаете DynamicTestJar.jar в свой путь к классам во время компиляции, но не во время выполнения программы? Не помещайте его в путь к классам во время компиляции, чтобы компилятор показал вам, где вы используете JAR статическим способом.

3
ответ дан 4 December 2019 в 21:10
поделиться

In Java, all classes are uniquely identified by classloader and FQN of the class.

ClassLoaders are hierarchical, meaning your dynamically loaded class has access to all of its parent's classes, but the parent doesn't have any direct access to its classes.

Now, if your child classloader defines a class that extends your parent, your parent's code can refer to that class only through reflection, or by the parent class.

If you think of all Java code as reflective, this becomes easier. IE:

Class.forName("MyException")

The current class doesn't have access to the the child classloader, so it can't do this.

0
ответ дан 4 December 2019 в 21:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: