Java模块化中资源加载须遵守模块边界,必须用Module.getResourceAsStream()跨模块访问,资源需置于导出包内或模块根目录,禁用ClassLoader全局查找,构建时需验证JAR中资源路径。

Java 模块化系统中加载非代码资源文件(如 .properties、.json、.xml、图片等)不能沿用传统 classpath 查找方式,必须尊重模块边界和可见性规则。核心原则是:资源不是“自动可见”的,必须通过模块自身的导出结构或显式模块 API 访问。
资源必须放在导出包内或模块根目录
模块系统不支持 exports resources,但资源能否被其他模块读取,取决于其物理位置是否在模块可暴露的路径中:
- 若资源放在
src/main/java/com/example/config/app.properties,且module-info.java中声明了exports com.example.config;,则其他模块可通过Class.getResource("/com/example/config/app.properties")安全访问 - 若资源放在
src/main/resources/logback.xml(Maven 标准路径),构建后会进入 JAR 的根目录;此时必须用Module.getResourceAsStream("logback.xml"),而非Class.getResource()—— 因为后者只搜索本模块导出包下的路径 - 把资源直接放在
src/main/java/version.txt(无包结构)也是可行的,它会被复制到模块输出的根路径,可用getClass().getModule().getResourceAsStream("version.txt")直接读取
跨模块访问必须用 Module API,禁用全局 ClassLoader
传统写法 Thread.currentThread().getContextClassLoader().getResourceAsStream(...) 在模块化环境下不可靠,尤其当目标资源不在调用方模块的导出范围内时,会静默返回 null:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 推荐方式:先获取目标模块引用,再调用其
getResourceAsStream() - 示例:
Module target = Class.forName("com.example.service.ServiceImpl").getModule(); InputStream is = target.getResourceAsStream("config.json"); - 若资源位于第三方模块(如
com.fasterxml.jackson.core),且该模块未导出含资源的包,即使文件物理存在也无法访问——这是模块系统的有意限制,不是 bug
构建与验证:确保资源真正进入模块输出
Maven/Gradle 默认会把 src/main/resources 内容复制进最终 JAR,但需手动验证:
立即学习“Java免费学习笔记(深入)”;
- 检查生成的模块 JAR 文件(如
target/myapp-1.0.jar),用jar -tf确认资源路径是否正确存在 - 若资源在
src/main/java下,需在pom.xml中显式配置资源目录:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<excludes><exclude>**/*.java</exclude></excludes>
</resource>
</resources>
</build> - 运行时若
getResourceAsStream()返回null,优先排查路径是否拼错、模块是否真的导出对应包、JAR 中是否真有该文件
读取时注意编码与异常处理
资源流本身不带编码信息,中文内容易乱码:
- 用
Properties.load(new InputStreamReader(is, StandardCharsets.UTF_8))显式指定 UTF-8 - 务必判空:
if (is == null) { throw new RuntimeException("Missing required resource: config.json"); } - 所有 IO 操作必须用
try-with-resources,避免流泄漏 - 静态代码块中加载失败应抛
ExceptionInInitializerError,带清晰上下文,便于定位问题

















