我有以下项目结构。
ProjectName
|
|---src
|
|---main
|
|---java
| |
| |---ModuleName
| |
| |---module-info.java
| |
| |---PackageName
| |
| |---Main.java
|
|---resources
|
|---ResourceParentFolder
|
|---ResourceSubFolderA
| |
| |---Resource1A.png
| |---Resource2A.png
| |---Resource3A.png
|
|---ResourceSubFolderB
|
|---Resource1B.png
|---Resource2B.png
|---Resource3B.png
我有一个 shell 脚本来编译代码,然后运行该代码。
javac \
--module-source-path="src/main/java" \
--module=ModuleName \
-d classes
java \
--module-path="classes;src/main/resources" \
--module=ModuleName/PackageName.Main
我还有一个 shell 脚本,可以将编译后的代码转换为模块化 jar,然后运行该 jar。
jar \
--verbose \
--create \
--file run/executable/jar/ProjectName.jar \
--main-class PackageName.Main \
-C classes/ModuleName . \
-C src/main/resources .
java \
--module-path="run/executable/jar" \
--module=ModuleName/PackageName.Main
java.lang.module.ModuleReader
,特别是它的 list() 方法,它允许我遍历我的模块及其内容。
如果我获取 jar 文件并尝试运行它,我就可以看到
ResourceParentFolder
的内容,但是当我仅运行已编译的代码时,对 list()
的调用仅返回 .class
文件。这是因为我的模块配置错误吗?或者这只是不受支持的功能?
再次,当作为 jar 运行时,
ModuleReader.list()
返回源代码和资源文件夹内容的递归列表,但它仅在作为编译代码运行时返回源代码。如何让编译后的代码也填充 ModuleReader.list()
?或者这只是不支持的功能,除非它在罐子或其他东西中?
需要明确的是,我很清楚有一百万种和其他一种获取资源的方法。但我想知道是否可以按照我上面要求的方式做到这一点。如果没有,那为什么?
编辑——详细说明我的一些失败的尝试。
我尝试将
src/main/resources
目录复制到 classes
中,这是我的模块在变成 jar 之前的位置。不幸的是,它什么也没捡到。
我也尝试过做
--patch-modules
,但也失败了,但是有错误。
error: no source files
这是我使用的命令。
javac --patch-module ModuleName=src/main/resources
您尝试将
src/main/resources
添加到模块路径将无法按您的预期工作。简单地将目录添加到模块路径并不会使它们的内容成为模块的一部分。您要么需要将资源和已编译的类放在同一目录中,或者放在 JAR 文件中,这就是您的代码在打包在 JAR 中时可以工作的原因,要么需要使用 --patch-module
。
这是上述两种方法的示例。请注意,所有命令都是使用 PowerShell Core 7.2.13 和 Java 20.0.1 在 Windows 10 上的项目目录中执行的。
虽然下面的输出列出了资源目录以及实际资源,但这并不能保证。事实上,我相信当模块打包在运行时映像中时,这些相同的目录不会列出(通过
jlink
/jpackage
)。
两种方法的源代码相同。
模块信息
module app {}
示例.Main
package sample;
public class Main {
public static void main(String[] args) throws Exception {
var module = Main.class.getModule();
var reference = module.getLayer()
.configuration()
.findModule(module.getName())
.orElseThrow()
.reference();
try (var reader = reference.open()) {
reader.list().forEach(System.out::println);
}
}
}
目录结构
<PROJECT-DIR>
|
\---src
\---main
+---java
| \---app
| | module-info.java
| |
| \---sample
| Main.java
|
\---resources
\---app
\---foo
res.txt
注意,我在
app
下添加了 src/main/resources
“模块目录”,只是为了镜像与 src/main/java
下所做的相同操作。这不是必需的,尽管没有它需要稍微更改下面使用的命令(只需更改路径)。
命令
& javac --module-source-path src\main\java --module app -d build\classes
& robocopy src\main\resources build\classes /S /NJS /NJH /NDL /NFL
& java --module-path build\classes --module app/sample.Main
注意:
/NJS /NJH /NDL /NFL
样板只是为了让 robocopy
保持安静。可能有更好的选择,不确定。
输出
foo/
foo/res.txt
module-info.class
sample/
sample/Main.class
如您所见,资源
foo/res.txt
(甚至目录foo/
)已列出。
目录结构(运行命令后)
<PROJECT-DIR>
|
+---build
| \---classes
| \---app
| | module-info.class
| |
| +---foo
| | res.txt
| |
| \---sample
| Main.class
|
\---src
\---main
+---java
| \---app
| | module-info.java
| |
| \---sample
| Main.java
|
\---resources
\---app
\---foo
res.txt
--patch-module
命令
& javac --module-source-path src\main\java --module app -d build\classes
& java --module-path build\classes --patch-module app=src\main\resources\app --module app/sample.Main
输出
module-info.class
sample/
sample/Main.class
foo/
foo/res.txt
再次,您可以看到资源
foo/res.txt
(和目录foo/
)已列出。
目录结构(运行命令后)
<PROJECT-DIR>
|
+---build
| \---classes
| \---app
| | module-info.class
| |
| \---sample
| Main.class
|
\---src
\---main
+---java
| \---app
| | module-info.java
| |
| \---sample
| Main.java
|
\---resources
\---app
\---foo
res.txt