Java不会从清单定义的类路径中加载类

问题描述 投票:0回答:1

我正在使用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 ''
}

这将使用以下META-INF / MANIFEST.MF正确生成一个jar文件:

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

并且在此jar文件的根目录中,确实存在清单中引用的sqlite-jdbc-3.7.2.jar文件;我手动验证了此jar文件中是否存在一个名为org.sqlite.JDBC的类。

但是,使用java -jar jarfile.jar运行生成的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有一个静态初始化程序,该初始化程序加载org.sqlite.JDBC,这在堆栈跟踪中称为SqliteConnectionManager.java:14

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

我用OpenJDK 11和OpenJ9 11对此进行了测试,结果相同。我得出的结论是我做错了,但我不明白该怎么办。

java jar classpath
1个回答
1
投票

[我发现我做错了:文档清楚地说明(doh!)Class-Path指令在本地文件系统或网络中寻找其他类路径资源。

JVM不支持从主jar文件中包含的jar文件中加载其他类,因此需要自定义的加载代码。一个更简单的替代方法是从jar文件中解压缩其他类,然后直接包含它们。以这种方式构建的jar文件称为“胖jar”(或“超级jar”)。

有一个名为Shadow的Gradle插件,可以无需任何进一步配置即可构建这样的jars。

© www.soinside.com 2019 - 2024. All rights reserved.