Устранение неполадок при открытии банки JVM
У меня есть крошечная Java-программа "Test.java", которая опирается на класс "TestLib", который находится в отдельном файле jar в том же каталоге.
Структура "Проекта":
$ ls
Test.java Test.class testlib.jar
Test.java и testlib.jar - это заглушки, которые воспроизводят проблему с classpath, которую я вижу в гораздо более крупном приложении. Все, что делает Test.java, - это создание экземпляра класса из testlib.jar и выход:
import com.TestLib;
public class Test {
public static void main(String[] args) {
System.out.println("Before instantiating testlib");
new TestLib();
System.out.println("After instantiating testlib");
}
}
У моего тестового фляги есть класс тестовой библиотеки:
$ unzip -l testlib.jar
...
com/TestLib.class
...
(Jar содержит много других классов и файлов метаинформации, здесь опущено)
Я могу скомпилировать этот маленький проект с: javac Test.java -classpath .:testlib.jar
Я пытаюсь запустить его с помощью команды java -classpath .:testlib.jar Test
и получите вывод:
Before instantiating testlib
Exception in thread "main" java.lang.NoClassDefFoundError: com/TestLib at Test.main(Test.java:6)
Он не может найти "TestLib"!
Интересно, когда я добавляю -verbose
флаг моего вызова Java, я вижу, что он никогда даже не открывает мою библиотеку JAR для поиска там TestLib:
$ java -verbose -classpath .:testlib.jar Test 2>&1 | grep "Opened"
[Opened /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/rt.jar]
[Opened /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/jsse.jar]
[Opened /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/jce.jar]
Что определяет, открывает ли JVM файл jar для поиска отсутствующего класса? Существуют ли какие-либо META-INF или другое содержимое банки, которое препятствует открытию банки JVM? Какие действия следует предпринять для устранения неполадок, почему "TestLib" не найден во время выполнения?
1 ответ
В моем случае я видел это поведение из-за META-INF/INDEX.LIST
файл присутствует в моем JAR.
Этот файл (если имеется) содержит список файлов классов, доступных в JAR. Мой файл INDEX.LIST был неполным и не упоминал мой TestLib
учебный класс. Поэтому, когда ClassLoaders пришел посмотреть, они поверили в список содержимого банку в INDEX.LIST
и сообщил, что они не смогли найти класс.
(Я закончил с неполным INDEX.LIST
из-за какого-то нетрадиционного (читай, некорректного) использования плагина Gradle "shadow". По умолчанию плагин shadow удаляет все файлы INDEX.LIST при создании затененного фляги, но я случайно настроил его так, чтобы он этого не делал. Так что, если вы видите такое поведение в затененной банке, это, вероятно, причина.)