JavaFX+jlink构建后遇ProviderNotFoundException及栈追踪问题求助
问题:jlink构建JavaFX应用后Jar文件处理异常及栈追踪无代码行问题
背景信息
使用jlink将JavaFX应用与openjdk-18.0.2.1组件一同构建,构建脚本如下:
import org.gradle.internal.os.OperatingSystem plugins { id 'java' id 'application' id 'org.openjfx.javafxplugin' version '0.0.13' id 'org.beryx.jlink' version '2.25.0' } group 'com.prototype' version '1.0' repositories { mavenCentral() } ext { junitVersion = '5.9.0' } java { toolchain { languageVersion.set(JavaLanguageVersion.of(18)) } //withSourcesJar() } tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } application { mainModule = 'com.prototype.catassistant' mainClass = 'com.prototype.catassistant.CatAssistantApplication' } javafx { version = '18.0.1' modules = ['javafx.controls', 'javafx.fxml', 'javafx.web'] } dependencies { // https://mvnrepository.com/artifact/org.jetbrains/annotations implementation 'org.jetbrains:annotations:23.0.0' implementation('org.controlsfx:controlsfx:11.1.1') // https://www.benf.org/other/cfr/api/index.html // https://mvnrepository.com/artifact/org.benf/cfr implementation 'org.benf:cfr:0.152' // https://mvnrepository.com/artifact/com.google.code.gson/gson implementation 'com.google.code.gson:gson:2.9.1' testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}") } test { useJUnitPlatform() } jlink { imageZip = project.file("${buildDir}/distributions/app-${javafx.platform.classifier}.zip") options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages'] jpackage { def currentOs = OperatingSystem.current() def imgType = currentOs.windows ? 'ico' : currentOs.macOsX ? 'icns' : 'png' icon = "src/main/resources/java.$imgType" if (currentOs.windows) { installerOptions += ['--win-per-user-install', '--win-dir-chooser', '--win-menu', '--win-shortcut', '--vendor', 'Prototype'] imageOptions += ['--win-console',/* '--icon', 'src/main/resources/icon.ico'*/] } } launcher { name = 'CatAssistant' } } jlinkZip { group = 'distribution' }
构建镜像后应用可正常启动,但处理Jar文件时抛出异常:
Caused by: java.nio.file.ProviderNotFoundException: Provider not found at java.base/java.nio.file.FileSystems.newFileSystem(Unknown Source) at java.base/java.nio.file.FileSystems.newFileSystem(Unknown Source) at com.prototype.catassistant@1.0/com.prototype.catassistant.helper.FileHelper.readJarFile(Unknown Source)
相关代码FileHelper#readJarFile:
// https://stackoverflow.com/a/37413531/10663941 public static void readJarFile(JarFile jar) { try (FileSystem system = FileSystems.newFileSystem(jar.getPath())) { // <- exception for (Path directory : system.getRootDirectories()) { try (Stream<Path> stream = Files.walk(directory)) { stream.filter(Predicate.not(Files::isDirectory)) .forEach(path -> jar.addEntry(path, read(system.getPath(path.toString())))); } } } catch (IOException ex) { AlertUtil.showExceptionDialog(ex); throw new RuntimeException(ex); } }
同时,构建后所有栈追踪均显示(Unknown Source)而非具体代码行,需一并解决。
解决方案
1. 解决ProviderNotFoundException异常
问题根源是jlink构建的运行时镜像默认未包含jdk.zipfs模块,该模块负责提供Zip/Jar文件系统的实现,而IDEA运行时使用完整JDK,因此不会出现此问题。
修复方式:
在jlink配置中添加jdk.zipfs模块,修改jlink任务的配置:
jlink { imageZip = project.file("${buildDir}/distributions/app-${javafx.platform.classifier}.zip") options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages'] // 添加需要包含的模块 modules += ['jdk.zipfs'] jpackage { def currentOs = OperatingSystem.current() def imgType = currentOs.windows ? 'ico' : currentOs.macOsX ? 'icns' : 'png' icon = "src/main/resources/java.$imgType" if (currentOs.windows) { installerOptions += ['--win-per-user-install', '--win-dir-chooser', '--win-menu', '--win-shortcut', '--vendor', 'Prototype'] imageOptions += ['--win-console',/* '--icon', 'src/main/resources/icon.ico'*/] } } launcher { name = 'CatAssistant' } }
或者,在你的模块描述文件module-info.java中添加对jdk.zipfs的依赖:
module com.prototype.catassistant { requires javafx.controls; requires javafx.fxml; requires javafx.web; // 添加这一行 requires jdk.zipfs; // 其他requires声明... }
2. 解决栈追踪显示(Unknown Source)问题
这是因为jlink的options中设置了--strip-debug参数,该参数会移除所有调试信息(包括行号)。
修复方式:
移除--strip-debug参数,或者替换为保留行号的调试信息选项:
jlink { imageZip = project.file("${buildDir}/distributions/app-${javafx.platform.classifier}.zip") // 移除--strip-debug,或者替换为--strip-native-debug-symbols(仅移除本地调试符号,保留Java行号) options = ['--compress', '2', '--no-header-files', '--no-man-pages'] // 或者:options = ['--strip-native-debug-symbols', '--compress', '2', '--no-header-files', '--no-man-pages'] jpackage { // 原jpackage配置... } launcher { name = 'CatAssistant' } }
注意:移除--strip-debug会使镜像体积略有增大,但能保留完整的调试信息;使用--strip-native-debug-symbols则可以在保留Java代码行号的同时,移除不影响Java调试的本地符号,平衡体积和调试需求。
内容的提问来源于stack exchange,提问作者The Prototype
相关产品推荐
相关产品推荐

