Java模块化(JPMS)下JavaFX应用资源加载失败求助
我明白你在把JavaFX应用迁移到JPMS时遇到的资源加载困扰——javaClass.getResource("/fxml/scene-splash.fxml")返回null,临时加classpath能解决但不符合模块化规范。咱们一步步来搞定这个问题:
问题根源
你的资源目录src/main/resources被单独放到了模块路径(-p参数)里,这会让它变成一个自动模块(没有module-info.java的模块)。而JPMS中,模块之间的资源访问是受限制的,主模块donim.application默认无法访问自动模块里的资源,这就是返回null的原因。临时加--class-path是把资源丢到非模块化的ALL-UNNAMED模块里,虽然能绕过去,但不是正规的模块化做法。
正确解决方案
1. 确保资源属于主模块的一部分
首先要让src/main/resources下的资源被打包到主模块donim.application中,也就是和module-info.java同目录。对于Gradle Kotlin项目,默认配置应该已经做了这件事,但可以检查build.gradle.kts的sourceSets配置确认:
sourceSets.main { kotlin.srcDir("src/main/kotlin") resources.srcDir("src/main/resources") }
这个配置会让Gradle把resources里的文件复制到编译输出目录(比如out/production/classes),这样资源就成为主模块的一部分了。
2. 修正IntelliJ的运行配置
修改你的运行配置,把模块路径(VM参数里的-p)改成只包含out/production/classes,不要单独加out/production/resources。因为资源已经被合并到classes目录里了,单独放会导致资源成为独立的自动模块,引发访问问题。
3. 保持正确的资源加载方式
只要资源属于主模块,你原来的加载代码完全可以正常工作:
javaClass.getResource("/fxml/scene-splash.fxml")
如果想更贴合模块化规范,也可以用模块类加载器加载:
Main::class.java.module.getResourceAsStream("/fxml/scene-splash.fxml")
验证步骤
你可以去out/production/classes目录下看看,是否存在fxml/scene-splash.fxml文件。如果有,说明资源已经正确合并到模块中;如果没有,重新执行Gradle的compileKotlin或者processResources任务,确保资源被复制过去。
额外注意
- 如果你需要让其他模块访问主模块的资源,需要在
module-info.java中用exports声明资源目录,比如:
不过你这里是自己模块内加载,不需要这个配置。exports fxml to javafx.fxml; - 你已经正确
opens了控制器包给javafx.fxml,这部分没问题,FXML可以正常访问控制器类。
内容的提问来源于stack exchange,提问作者Mahozad

