除了现有的答案之外,我还想举一个例子,说明不同资源目录名称的非类文件资源的封装规则。
getResourceAsStream的规范说,如果包名称是从其名称派生的,则资源被封装。
因此,如果资源的目录名称不是有效的 Java 标识符,则它不会被封装。这意味着如果一个模块有一个资源位于例如一个名为dir-1
(在其名称中包含无效字符-
)的目录下,它将始终可以从模块外部访问。
这是两个 Java 模块的示例(GitHub 中的源代码)。模块 1包含以下资源文件:
├── dir-3
│ └── resource3.txt
├── dir1
│ └── resource1.txt
├── dir2
│ └── resource2.txt
└── root.txt
和module-info.java
:
module module_one {
opens dir1;
}
模块 2需要模块1:module-info.java
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));
}
}
}
运行上面的类会得到以下输出:</p>
// 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无法加载它。
请注意,模块 2不能在dir1
和dir2
目录下包含自己的资源,因为它们已经封装在模块 1中。如果您尝试添加dir1
,您将收到以下错误:
Error occurred during initialization of boot layer
java.lang.LayerInstantiationException: Package dir1 in both module module_one and module module_two
这是一个Flyway 相关问题供参考。