模块化Java中子目录资源加载失败问题排查
问题描述
现有一个基于JPMS的模块化Java项目,相关代码与目录结构如下:
Main类代码
package resources; public class Main { public static void main(String[] args) { System.out.println(ClassLoader.getSystemResource("toplevel.txt")); System.out.println(ClassLoader.getSystemResource("/sub/nested.txt")); } }
module-info.java代码
module resources { exports resources; }
编译后目标目录结构
resources/Main.class sub/nested.txt toplevel.txt
程序运行输出
file:/C:/Eclipse/workspace-2023-03/resources/bin/toplevel.txt null
已知目标目录路径为/C:/Eclipse/workspace-2023-03/resources/bin/,但/sub/nested.txt始终无法被找到。使用JDK 17与Eclipse开发,非模块化项目的解决方案对此场景不适用,请问问题出在哪里?
原因及解决方案
在模块化项目(JPMS)中,资源访问规则与非模块化项目存在本质差异,核心问题出在模块资源的访问权限和路径写法上:
ClassLoader资源查找规则的变化
JPMS下,ClassLoader.getSystemResource接收的路径如果以/开头,会被自动忽略斜杠,视为相对于模块根的路径,但这不是核心问题——真正的障碍是:模块默认不会开放未声明的资源目录给类加载器访问。必须显式开放资源目录的访问权限
默认情况下,模块化项目中只有模块内的包(通过exports声明)和显式开放的资源目录(通过opens声明)才能被类加载器读取。你需要修改module-info.java,添加对sub目录的开放声明:module resources { exports resources; // 开放sub目录,允许所有模块读取其中的资源 opens sub to all; // 若仅需本模块内部访问,可简化为:opens sub; }修正资源路径写法
在模块化项目中,ClassLoader.getSystemResource不需要以/开头,直接使用相对模块根的路径即可。修正后的代码:System.out.println(ClassLoader.getSystemResource("sub/nested.txt"));更规范的模块化资源获取方式
推荐直接通过当前模块获取资源,避免类加载器的路径歧义,代码示例:// 获取资源URL System.out.println(Main.class.getModule().getResource("sub/nested.txt")); // 若需读取资源流,使用: // Main.class.getModule().getResourceAsStream("sub/nested.txt");
总结:模块化项目中,资源目录必须通过opens声明开放访问权限,同时资源路径无需添加开头的/,遵循模块根相对路径的写法即可解决问题。
内容的提问来源于stack exchange,提问作者Arend
相关产品推荐
相关产品推荐

