Java не загружает классы из определенного пути к классам - PullRequest
0 голосов
/ 13 января 2020

Я использую Gradle для создания проекта. Это мой build.gradle:

plugins { id 'application' }

group 'my.group'
version '1.0'

sourceCompatibility = 1.11

repositories { mavenCentral() }

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    implementation group: 'org.xerial', name: 'sqlite-jdbc', version: '3.7.2'
}

mainClassName = 'my.group.App'

jar {
    manifest {
        attributes(
            'Main-Class': mainClassName,
            'Class-Path': configurations.runtimeClasspath.files.collect { it.name }.join(' ')
        )
    }

    from configurations.runtimeClasspath
    into ''
}

Это правильно генерирует файл jar со следующим META-INF / MANIFEST.MF:

Manifest-Version: 1.0
Main-Class: my.group.App
Class-Path: sqlite-jdbc-3.7.2.jar
[newline]

И в root этого jar-файл, действительно есть файл sqlite-jdbc-3.7.2.jar, указанный в манифесте; Я вручную проверил, что внутри этого файла jar есть класс с именем org.sqlite.JDBC.

Однако запуск созданного jar с java -jar jarfile.jar приводит к:

Exception in thread "main" java.lang.ExceptionInInitializerError
        at my.group.LEManagerSqlite.<init>(LEManagerSqlite.java:64)
        at my.group.LEManager.createLEManager(LEManager.java:80)
        at my.group.GuiFrame.<init>(GuiFrame.java:60)
        at my.group.App.openFromFile(App.java:23)
        at my.group.App.main(App.java:19)
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: org.sqlite.JDBC
        at my.group.SqliteConnectionManager.<clinit>(SqliteConnectionManager.java:17)
        ... 5 more
Caused by: java.lang.ClassNotFoundException: org.sqlite.JDBC
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:315)
        at my.group.SqliteConnectionManager.<clinit>(SqliteConnectionManager.java:14)
        ... 5 more

Для справки: SqliteConnectionManager имеет инициализатор stati c, который загружает org.sqlite.JDBC, то есть то, что в трассировке стека упоминается как SqliteConnectionManager.java:14:

Class.forName("org.sqlite.JDBC");

Я проверил это с OpenJDK 11 и OpenJ9 11, с идентичными результатами , Я делаю вывод, что делаю что-то не так, но не могу понять, что.

1 Ответ

1 голос
/ 13 января 2020

Я выяснил, что я делал неправильно: в документации четко сказано (дох!), Что директива Class-Path ищет дополнительные ресурсы classpath в локальной файловой системе или в сети.

Загрузка дополнительных классов из jar файлы, включенные в основной файл jar, не поддерживаются JVM, поэтому для них требуется специальный код загрузки. Более простая альтернатива - распаковать дополнительные классы из файлов jar и включить их напрямую. Файл jar, созданный таким образом, называется «толстым jar» (или «uber jar»).

Существует плагин Gradle, называемый Shadow, который может создавать такие jar без дальнейшей настройки.

...