关键在于正确解析模块内资源路径:需用ModuleReader.open()安全读取,或通过ClassLoader.getResource()获取URL后判断协议再转Path,避免直接Paths.get()非法路径。

用 Files.newInputStream 访问模块化路径下的变量资源,关键不在“新建输入流”本身,而在于**如何正确解析并定位到模块内实际的资源路径**。Java 的模块系统(JPMS)默认不暴露 module-path 下的资源为传统 classpath 资源,直接用 ClassLoader.getResourceAsStream() 可能失效;而 Files.newInputStream 操作的是文件系统路径(Path),因此必须先将模块内资源映射为可访问的 Path,再构造流。
确认资源是否在模块内且已导出
模块化项目中,资源文件(如 config.json、templates/)需放在模块源根下(如 src/main/java/com/example/res/ 或 src/main/resources/),并在 module-info.java 中通过 opens 或 exports 声明允许反射或资源访问:
-
opens com.example.res to java.base;(推荐用于含非 public 类型资源的包) -
exports com.example.res;(仅适用于类,不适用于纯资源)
注意:exports 对 .json/.xml 等资源文件无效,必须用 opens 才能让 getResource 或 getResources 正常工作。
用 ClassLoader + URL 转 Path 获取模块内资源路径
Files.newInputStream 需要一个 Path,但模块内资源通常以 jar:file:///... 或 jrt:/ 协议 URL 形式存在。不能直接传入 URL 字符串,需解析协议并转换:
- 对普通 JAR 模块:用
URL.toURI()→Paths.get(uri)(仅当 URL 是file:协议时有效) - 对运行时镜像模块(
jrt:/):无法转为Path,必须改用Class.getResourceAsStream() - 稳妥做法:优先尝试
Class.getResource("/res/config.json")得到 URL,若协议为file,再转Path;否则回退到流式读取
示例代码片段:
URL resourceUrl = MyClass.class.getResource("/res/config.json");
if (resourceUrl != null && "file".equals(resourceUrl.getProtocol())) {
Path path = Paths.get(resourceUrl.toURI());
try (InputStream is = Files.newInputStream(path)) {
// 处理流
}
}
使用 ModuleLayer 和 ModuleReader 安全读取模块资源(JDK 9+)
更符合模块语义的方式是通过 Module 获取 ModuleReader,它专为读取模块内资源设计,兼容 jrt:、jar: 和 file::
- 获取当前类所在模块:
MyClass.class.getModule() - 调用
module.getDescriptor().packages()确认资源所在包是否被 opens - 用
module.getReader().open("res/config.json")返回Optional
这种方式不依赖文件系统路径,也不要求资源在 classpath,是模块化场景下最健壮的资源访问方式。
避免常见陷阱
- 不要对
jar:file:/xxx.jar!/res/config.json直接调用Paths.get()—— 会抛InvalidPathException - 不要假设
System.getProperty("java.class.path")包含模块路径 —— 模块路径由--module-path控制,与 classpath 分离 - 开发阶段用 IDE 运行时,模块可能未真正封装为 JAR,资源以目录形式存在;生产环境打包后变为 JAR,行为一致的前提是正确配置
opens











