除了现有的答案,我想举例说明不同资源目录名称的非类文件资源的封装规则。
getResourceAsStream 的规范表明,如果 包名 是从其名称派生的,则资源被封装。
所以如果resource’s directory name is NOT a valid Java identifier,它没有被封装。这意味着如果一个模块有一个资源位于例如一个名为 dir-1 的目录下(在其名称中包含一个无效字符 -),它将始终可以从模块外部访问。
这是两个 Java 模块 (source code in GitHub) 的示例。 模块 1 包含以下资源文件:
├── dir-3
│ └── resource3.txt
├── dir1
│ └── resource1.txt
├── dir2
│ └── resource2.txt
└── root.txt
和module-info.java:
module module_one {
opens dir1;
}
模块 2 需要 module-info.java 中的 模块 1:
module module_two {
requires module_one;
}
并有一个用于加载各种资源文件的示例主类:
package module2;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
loadResource("root.txt", "From module's root directory");
loadResource("dir1/resource1.txt", "From opened package `dir1`");
loadResource("dir2/resource2.txt", "From internal package `dir2`");
loadResource("dir-3/resource3.txt", "From directory `dir-3` with non-Java name");
}
public static void loadResource(String name, String comment) throws IOException {
// module2 application class loader
final var classLoader = Main.class.getClassLoader();
try (var in = classLoader.getResourceAsStream(name)) {
System.out.println();
System.out.println("// " + comment);
System.out.println(name + ": " + (in != null));
}
}
}
运行上面的类会得到以下输出:
// From module's root directory
root.txt: true
// From opened package `dir1`
dir1/resource1.txt: true
// From internal package `dir2`
dir2/resource2.txt: false
// From directory `dir-3` with non-Java name
dir-3/resource3.txt: true
你可以看到根目录和dir-3目录的资源文件没有被封装,因此模块2可以加载它们。
包裹dir1被封装但无条件打开。 模块 2 也可以加载它。
包裹dir2被封装,没有打开。 模块 2 无法加载。
请注意,Module 2 不能在 dir1 和 dir2 目录下包含自己的资源,因为它们已经封装在 Module 1 中。如果您尝试添加dir1,您将收到以下错误:
Error occurred during initialization of boot layer
java.lang.LayerInstantiationException: Package dir1 in both module module_one and module module_two
这是一个Flyway related issue 供参考。