我有以下项目 struct .

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

在我的main方法中,我调用了java.lang.module.ModuleReader,特别是它的list()方法,它允许我遍历模块及其内容.

如果我使用JAR文件并try 运行它,我可以看到ResourceParentFolder的内容,但当我只运行编译后的代码时,对list()的调用只返回.class个文件.这是因为我的模块配置错误吗?或者这仅仅是不受支持的功能?

同样,当作为JAR运行时,ModuleReader.list()返回源代码和资源文件夹内容的递归列表,但当作为编译代码运行时,它只返回源代码.如何让编译后的代码也填充ModuleReader.list()?或者,这只是不支持的功能,除非它是在一个罐子或其他什么?

需要说明的是,我很清楚有100万零1种其他方法可以获取资源.但我想知道是否有可能按照我上面要求的方式来做.如果不是,为什么呢?

编辑--详细介绍我一些失败的try .

我try 将src/main/resources目录复制到classes中,这是模块变成JAR之前的位置.不幸的是,它什么也没拿到.

我也试着做--patch-modules次,但也失败了,但有一个错误.

error: no source files

以下是我使用的命令.

javac   --patch-module ModuleName=src/main/resources

推荐答案

您try 将src/main/resources添加到模块路径的try 将不会如您预期的那样工作.简单地将目录添加到模块路径并不会使它们的内容成为模块的一部分.您需要执行以下任一操作:

  1. 将类和资源放在同一位置,无论是目录还是JAR文件.这使得资源成为模块的一部分.请注意,这就是JAR文件按预期工作的原因,因为您已经将类和资源打包在同一个JAR文件中.

    这种方法本质上就是"应该怎么做".

  2. 在运行时使用--patch-module.注这种方法不需要将资源文件移动到任何地方.

    如果您试图对仅包含资源的包执行opens操作,则还需要在编译时使用此参数;否则,将发出一条警告,说明该包不存在.不过,如果我没记错的话,在这个场景中(其中opens包本身不是模块的一部分),即使使用--patch-module参数,您也会在运行时得到一个错误.

综上所述,我确实推荐对重要的Java项目使用构建工具(例如,Maven、Gradle等).他们通常会为您处理这一切.


Example

下面是上面讨论的两种方法的一个示例.注所有命令都是使用PowerShell Core 7.2.13和Java 20.0.1在Windows 10的项目目录中执行的.

虽然下面的输出列出了资源directories以及实际资源,但这是不能保证的.事实上,如果我没有记错的话,在将模块打包到运行时映像(通过jlink/jpackage)时,这些相同的目录将不会列出.

源代码

这两种方法的源代码是相同的.

module-info

module app {}

sample.Main

package sample;

public class Main {

    public static void main(String[] args) throws Exception {
        var module = Main.class.getModule();
        var reference = module.getLayer()     // ModuleLayer
                .configuration()              // Configuration
                .findModule(module.getName()) // Optional<ResolvedModule>
                .orElseThrow()                // ResolvedModule
                .reference();                 // ModuleReference
        try (var reader = reference.open()) {
            reader.list().forEach(System.out::println);
        }
    }
}

Directory structure

<PROJECT-DIR>
|
\---src
    \---main
        +---java
        |   \---app
        |       |   module-info.java
        |       |
        |       \---sample
        |               Main.java
        |
        \---resources
            \---app
                \---foo
                        res.txt

注我在src/main/resources下添加了一个app"模块目录",只是为了反映src/main/java下所做的事情(如果您想在javac下使用--module-source-path--module,这是必需的).这不是必需的,尽管不这样做需要稍微更改下面使用的命令(只需更改路径).

方法1-合并类和资源

Commands个个

& javac --module-source-path src\main\java --module app -d build\classes
& robocopy src\main\resources build\classes /S > NUL
& java --module-path build\classes --module app/sample.Main

Output个个

foo/
foo/res.txt      
module-info.class
sample/
sample/Main.class

如您所见,列出了资源foo/res.txt(甚至是目录foo/).

Directory structure (after running commands)个个

<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 

Approach 2 - Use --patch-module

Commands个个

& 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

Output个个

module-info.class
sample/
sample/Main.class
foo/
foo/res.txt

同样,您可以看到列出了资源foo/res.txt(以及目录foo/).

Directory structure (after running commands)个个

<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

Java相关问答推荐

获取拦截器内部的IP地址

我们如何直接使用kerminldap服务票证来通过ldap进行身份验证并形成LDAP上下文

将具有多个未知字段的SON映射到Java POJO

填写文本字段后锁定PDF

springboot start loge change

Exe4j创建的应用程序无法再固定在任务栏Windows 11上

所有 case 一起输入时输出错误,而单独放置时输出正确

当涉及到泛型时,类型推理在Java中是如何工作的?

把一条整型短裤和两条短裤装成一条长的

如何在Java记录中设置BigDecimal类型属性的精度?

Java ArrayList的整数和数组的泛型

如何为JavaFX Spring Boot应用程序制作Windows/MacOS/Linux安装程序

如何从日志(log)行中删除包名称?

由于在生成器模式中使用泛型,lambda表达式中的返回类型错误

如何在我的世界中为互动增加冷却时间?

错误:未找到扩展元素在JBossEAP 7.2中安装FUSE时出错

嘲笑黄瓜中的对象

视图被推出线性布局-Android

这是JavaFX SceneBuilder的错误吗?

当我将鼠标悬停在javafxTextArea上时,如何更改鼠标光标?