从外部模块访问资源文件
从外部模块访问资源文件
到目前为止,在非模块化的Java中,您只需将文件放在src/main/java/resources
中,确保它在类路径中,然后使用以下代码在类路径的几乎任何位置加载它:
file = getClass().getClassLoader().getResourceAsStream("myfilename");
现在有了模块,情况变得复杂了。
我的项目设置如下:
module playground.api { requires java.base; requires java.logging; requires framework.core; }
配置文件放在src/main/resources/config.yml
中。
项目使用以下命令运行:
java -p target/classes:target/dependency -m framework.core/com.framework.Main
由于主类不驻留在我的项目中,而是在一个外部框架模块中,它无法看到config.yml
。现在的问题是,是否有办法将我的配置文件放入模块中或打开它?我是否需要改变框架上游中加载文件的方式?
我尝试在module-info中使用"exports"或"opens",但它要求使用包名,而不是文件夹名。
如何以最佳实践的方式实现这一点,使其像在Java 8中一样工作,并尽可能少地进行更改?
在使用java
命令启动应用程序时,您可以通过以下方式访问资源文件:-
java -p target/classes:target/dependency -m framework.core/com.framework.Main
- 您使用
-p
选项指定了模块路径,该选项是--module-path
的替代,它将在target/classes和target/dependency中查找模块。 - 同时,使用
-m
选项作为--module
的替代,指定了要解析的初始模块,名称为framework.core
,并将主类com.framework.Main
明确列在要执行的模块图中。
现在,问题似乎是模块framework.core
没有requires
或读取playground.api
模块,因此模块图不包括包含实际资源config.yml
的所需模块。
如建议的,在启动期间列出模块解析输出的好方法是使用--show-module-resolution
选项。
我只是天真地尝试打开src/main/resources,当然无法编译
由于您的模块中的资源位于根级别,因此它未封装,不需要打开或导出给任何其他模块。
在您的情况下,您只需要确保模块playground.api
出现在模块图中,然后该资源将对应用程序可访问。要指定要解析的根模块以及初始模块,可以使用--add-modules
选项。
因此,解决方案为:
java --module-path target/classes:target/dependency --module framework.core/com.framework.Main --add-modules playground.api --show-module-resolution
当在命名模块中运行时,ClassLoader的getResource方法具有非常令人惊讶的行为。getResourceAsStream方法也同样有问题。我在升级应用程序到命名模块时遇到了这个问题。
如果你的代码在一个命名模块中,并且通过ClassLoader的getResource方法访问资源,那么该资源的包必须无条件地被打开。否则,即使资源位于同一个模块中,你也无法检索到该资源。
这与Class的getResource方法的行为不同-请注意区别。Class的getResource方法是直接的,没有这个令人讨厌的意外情况。
关于getResource的所有说法也适用于getResourceAsStream方法。
因此,我建议始终使用Class上的getResource方法,而不是ClassLoader上的方法。
还可以参考我对Class.getResource和ClassLoader.getResource之间的区别是什么的回答。
问题的出现原因:
问题是在一个模块中访问另一个模块中的资源文件时遇到的。在给定的示例中,有两个模块:FrameworkCore和PlaygroundApi。FrameworkCore模块想要访问PlaygroundApi模块中的config.yml文件。然而,由于模块的封装性,FrameworkCore模块无法直接访问PlaygroundApi模块中的资源文件。
解决方法:
为了解决这个问题,可以使用以下几种方法来访问外部模块的资源文件:
1. 如果知道资源文件的名称,可以使用ClassLoader的getSystemResources()方法来扫描模块路径,获取资源文件的URL。示例代码如下:
ClassLoader.getSystemResources(resourceName)
2. 如果知道包含资源文件的类,可以使用Class.forName()方法来获取资源文件的输入流。示例代码如下:
Class.forName(className).getResourceAsStream(resourceName)
3. 如果知道包含资源文件的模块名称,可以使用ModuleLayer.boot().findModule()方法来获取模块并获取资源文件的输入流。示例代码如下:
ModuleLayer.boot().findModule(moduleName).getResourceAsStream(resourceName)
以上是解决访问外部模块资源文件的方法。
文章完整内容如下:
在Java应用程序中,有时候需要在一个模块中访问另一个模块中的资源文件。然而,在模块的封装性限制下,直接访问外部模块的资源文件可能会遇到问题。下面我们将介绍一些访问外部模块资源文件的方法。
首先,可以使用ClassLoader的getSystemResources()方法来扫描模块路径,获取资源文件的URL。示例代码如下:
ClassLoader.getSystemResources(resourceName)
其次,如果知道包含资源文件的类,可以使用Class.forName()方法来获取资源文件的输入流。示例代码如下:
Class.forName(className).getResourceAsStream(resourceName)
另外,如果知道包含资源文件的模块名称,可以使用ModuleLayer.boot().findModule()方法来获取模块并获取资源文件的输入流。示例代码如下:
ModuleLayer.boot().findModule(moduleName).getResourceAsStream(resourceName)
通过以上方法,我们可以解决在一个模块中访问另一个模块的资源文件的问题。
举个例子来说明,假设我们有两个模块:FrameworkCore和PlaygroundApi。FrameworkCore模块想要访问PlaygroundApi模块中的config.yml文件。我们可以使用上述方法来实现。
在FrameworkCore模块的Main.java文件中,我们可以使用ClassLoader的getSystemResources()方法来获取config.yml文件的URL,并通过URL的openStream()方法获取输入流,代码如下:
URL url = ClassLoader.getSystemResources("config.yml").nextElement(); InputStream is = url.openStream(); Main.read(is);
另外,我们也可以使用Class.forName()方法来获取资源文件的输入流,代码如下:
InputStream is = Class.forName("com.playground.api.App").getResourceAsStream("/config.yml"); Main.read(is);
还可以使用ModuleLayer.boot().findModule()方法来获取PlaygroundApi模块并获取资源文件的输入流,代码如下:
Optional<Module> specificModule = ModuleLayer.boot().findModule("PlaygroundApi"); specificModule.ifPresent(module -> { InputStream is = module.getResourceAsStream("config.yml"); Main.read(is); });
通过这些方法,我们可以在FrameworkCore模块中成功访问PlaygroundApi模块的资源文件。
在Java应用程序中,访问外部模块的资源文件可能会受到模块的封装性限制。为了解决这个问题,我们可以使用ClassLoader和Module API提供的方法来获取外部模块的资源文件。通过这些方法,我们可以轻松地在一个模块中访问另一个模块的资源文件,实现模块间的资源共享。