Java 不从 list 定义的类路径加载类

标签 java jar classpath

我正在使用 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文件的根目录中,确实存在 list 中引用的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 对此进行了测试,结果相同。我得出结论,我做错了什么,但我不明白是什么。

最佳答案

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

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

有一个名为 Shadow 的 Gradle 插件,无需任何进一步配置即可构建此类 jar。

关于Java 不从 list 定义的类路径加载类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59715583/

相关文章:

java - 使用 Hashmap 进行字符串分组

java - 关闭流而不将其分配给变量 [Java]

java - Inno 安装程序 : Removing files installed by previous version

java - 从现有 jar 创建 maven 工件的最佳方法

java - 如何在 Eclipse 上使用我的 java 类文件更新外部 jar

java - 如何在 NetBeans 中查看有关鼠标悬停的 Javadoc 文档?

java - 为运行 java 镜像的 pod 启用其余通信

java - 使用类路径在运行时编译类

java - 使用由 Java Bootstrap 类加载器加载的库的替代方案

java - 访问 .jar 文件中的图像文件时出现 NullPointerException