资源文件放在构建Java 9模块的Gradle项目中的哪个位置? [英] Where do resource files go in a Gradle project that builds a Java 9 module?

查看:185
本文介绍了资源文件放在构建Java 9模块的Gradle项目中的哪个位置?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

从IDEA 2018.2.1开始,IDE从模块化的依赖项开始错误突出显示包模块图中的
。我在项目中添加了一个 module-info.java 文件,并添加了
必需需要语句,但是我现在无法在 src / main / resources 目录中访问
资源文件。

As of IDEA 2018.2.1, the IDE starts error-highlighting packages "not in the module graph" from dependencies that have been modularized. I added a module-info.java file to my project and added the requisite requires statements, but I'm now having trouble accessing resource files in my src/main/resources directory.

(有关完整示例,请参阅此GitHub项目。)

(For a complete example, see this GitHub project.)

当我使用 ./ gradlew run ./ gradlew installDist +结果
包装器脚本,我能够读取资源文件,但是当我从IDE运行我的
应用程序时,我不是。

When I used ./gradlew run or ./gradlew installDist + the resulting wrapper script, I was able to read resource files, but when I ran my app from the IDE, I wasn't.

I向JetBrains提交了问题
,我所学到的是IDEA是使用模块
路径,而Gradle默认使用类路径。通过将以下块添加
到我的 build.gradle ,我可以将Gradle
添加到 ...不是能够读取任何资源文件。

I filed an issue with JetBrains, and what I learned was that IDEA was using the module path, while Gradle, by default, was using the classpath. By adding the following block to my build.gradle, I was able to get Gradle to also … not be able to read any resource files.

run {
    inputs.property("moduleName", moduleName)
    doFirst {
        jvmArgs = [
                '--module-path', classpath.asPath,
                '--module', "$moduleName/$mainClassName"
        ]
        classpath = files()
    }
}

我试过 export - 将我感兴趣的资源目录作为
包,并在编译时出现构建失败:

I tried export-ing the resource directory I was interested in as a "package", and at compile time got a build failure with:


错误:包是空的或不存在:mydir

error: package is empty or does not exist: mydir

使用打开而不是 exports 得到了同样的错误,尽管将
降级为警告。

Using opens instead of exports got the same error, though downgraded to a warning.

我甚至尝试移动 src / main / java下的 mydir 资源目录
但这产生了相同的错误/警告,并且还导致
资源未被复制到 build 目录。

I even tried moving the mydir resource directory under src/main/java, but this produced the same errors / warnings, and also resulted in the resources not being copied to the build directory.

Java 9中的资源应该放在哪里,如何访问它们?

Where are resources supposed to go in Java 9, and how do I access them?

注意:在继续
研究问题后,我已经大大编辑了这个问题。在最初的问题中,我还试图
弄清楚如何列出资源目录中的文件,但在调查的
课程中,我确定这是一个红鲱鱼 -
首先,因为读取资源目录仅在从$ code> file:/// URL读取
资源时才有效(可能甚至不是这样),
和第二个因为普通文件也没有工作,所以很明显
问题一般是资源文件而不是
目录。

Note: I've edited this question considerably after continuing to research the problem. In the initial question, I was also trying to figure out how to list the files in a resource directory, but in the course of the investigation I determined that was a red herring -- first, because reading resource directories only works when the resource is being read from a file:/// URL (and maybe not even then), and second because plain files weren't working either, so clearly the problem was with resource files in general and not specifically with directories.

解决方案:

Slaw的答案,我将以下内容添加到 build.gradle

Per Slaw's answer, I added the following to build.gradle:

// at compile time, put resources in same directories as classes
sourceSets {
  main.output.resourcesDir = main.java.outputDir
}

// at compile time, include resources in module
compileJava {
  inputs.property("moduleName", moduleName)
  doFirst {
    options.compilerArgs = [
      '--module-path', classpath.asPath,
      '--patch-module', "$moduleName=" 
        + files(sourceSets.main.resources.srcDirs).asPath,
      '--module-version', "$moduleVersion"
    ]
    classpath = files()
  }
}

// at run time, make Gradle use the module path
run {
  inputs.property("moduleName", moduleName)
  doFirst {
    jvmArgs = [
      '--module-path', classpath.asPath,
      '--module', "$moduleName/$mainClassName"
    ]
    classpath = files()
  }
}

附注:有趣的是,如果我继续添加Slaw的代码,使运行任务针对JAR执行,尝试在运行任务中读取资源目录 InputStream 抛出 IOException 而不是提供文件列表。 (对于JAR,它只是一个空的 InputStream 。)

Side note: Interestingly, if I don't continue on to add Slaw's code that makes the run task execute against the JAR, attempting to read a resource directory InputStream in the run task now throws an IOException instead of providing the list of files. (Against the JAR, it simply gets an empty InputStream.)

推荐答案

来自 Gradle的史诗关于Jigsaw支持我已经了解了一个插件可以简化下面描述的过程: gradle-modules-plugin 。史诗还提到了其他插件,如电锯(这是实验拼图插件的一个分支)。不幸的是,我还没有尝试过它们,所以我无法评论他们处理资源的情况,如果有的话。

From Gradle's epic regarding Jigsaw support I've learned of a plugin that may ease the process described below: gradle-modules-plugin. The epic also mentions other plugins such as chainsaw (which is a fork of the experimental-jigsaw plugin). Unfortunately, I haven't tried any of them as of yet so I can't comment on how well they handle resources, if at all.

在您的赏金中,您需要有关使用Gradle和Jigsaw模块处理资源的正确方法的官方文档。答案,据我所知,没有正确的方法,因为Gradle 仍然(从4.10-rc-2开始)没有Jigsaw模块的一流支持。您最接近的是构建Java 9模块文档。

In your bounty you request official documentation about "the right way" to handle resources with Gradle and Jigsaw modules. The answer, as far as I know, is that there is no "right way" because Gradle still (as of 4.10-rc-2) doesn't have first-class support for Jigsaw modules. The closest you get is the Building Java 9 Modules document.

但是,提及这是关于从模块内部访问资源(即不是从外部模块)。使用简单的 build.gradle 配置,这不应该太难修复。

However, you mention this is about accessing resources from within a module (i.e. not from external modules). This shouldn't be too hard to fix with a simple build.gradle configuration.

默认情况下,Gradle分离类和资源的输出目录。它看起来像这样:

By default, Gradle separates the output directories for classes and resources. It looks something like this:

build/
|--classes/
|--resources/

使用运行任务时 classpath sourceSets.main.runtimeClasspath 的值。此值包括两个目录,这是因为类路径的工作方式。您可以将其视为类路径只是一个巨大的模块。

When using the run task the classpath is the value of sourceSets.main.runtimeClasspath. This value includes both directories and this works because of the way the classpath works. You can think of it like the classpath is just one giant module.

然而,这在使用模块路径时不起作用,因为从技术上讲, resources 中的文件不属于在中的模块。我们需要一种方法来告诉模块系统 resources 是模块的一部分。幸运的是,有 - patch-module 。此选项将(引自 java --help-extra ):

This doesn't work, however, when using the modulepath because technically the files inside resources do not belong to the module that's inside classes. We need a way to tell the module system that resources is part of the module. Luckily, there's --patch-module. This option will (quoted from java --help-extra):


覆盖或者在JAR文件或目录中使用类和资源扩充模块。

override or augment a module with classes and resources in JAR files or directories.

并且具有以下格式(我假设; 分隔符取决于平台):

And has the following format (I'm assuming the ; separator is platform dependent):

--patch-module <module>=<file>(;<file>)*

允许您的模块访问它自己的资源只需配置你的运行任务,如下所示:

To allow your module to access it's own resources simply configure your run task like so:

run {
    input.property('moduleName', moduleName)
    doFirst {
        jvmArgs = [
                '--module-path', classpath.asPath,
                '--patch-module', "$moduleName=" + files(sourceSets.main.output.resourcesDir).asPath,
                '--module', "$moduleName/$mainClassName"
        ]
        classpath = files()
    }
}

这就是我一直在做的事情,到目前为止效果还不错。

This is how I've been doing it and it has worked out pretty well so far.

但是,当从Gradle启动应用程序时,如何允许外部模块从模块访问资源?这需要更多参与。

But how do you allow external modules to access resources from your module when launching the application from Gradle? This gets a little more involved.

当你想允许外部模块访问资源时你的模块必须打开(请参阅 Eng.Fouad的回答)至少读取模块的资源包(这仅适用于 封装 资源)。但是,正如您所发现的,这会导致编译警告和运行时错误。

When you want to allow external modules to access resources your module must opens (see Eng.Fouad's answer) the resource's package to at least the reading module (this only applies to encapsulated resources). As you've discovered, however, this leads to compilation warnings and runtime errors.


  1. 编译警告是因为您正在尝试打开根据模块系统不存在的包。


    • 这是预期的,因为默认情况下编译时不包括资源目录(假设只有资源的包)。

  1. The compilation warning is because you are trying to opens a package that doesn't exist according the the module system.
    • This is to be expected since the resources directory is not included when compiling by default (assuming a resource-only package).

  • 再次假设只有资源包。

  • 即使使用,也会发生这种情况。 -patch-module 上面提到的选项。我猜模块系统在应用补丁之前会进行一些完整性检查。

  • Again, assuming a resource-only package.
  • This occurs even with the --patch-module option mentioned above. I guess that the module system does some integrity checking before applying the patch.

注意:仅限资源是指没有 .java / .class 文件的软件包。

Note: By "resource-only" I mean packages that have no .java/.class files.

要修复编译警告,您只需再次使用 - patch-module compileJava 任务。这次你将使用资源的源目录而不是输出目录。

To fix the compilation warning you just have to use --patch-module again inside the compileJava task. This time you'll use the resources' source directories rather than the output directory.

compileJava {
    inputs.property('moduleName', moduleName)
    doFirst {
        options.compilerArgs = [
                '--module-path', classpath.asPath,
                '--patch-module', "$moduleName=" + files(sourceSets.main.resources.srcDirs).asPath,
                '--module-version', "$version"
        ]
    }
}

对于运行时错误,有几个选项。第一个选项是将资源输出目录与类的输出目录合并。

For the runtime error there are a couple of options. The first option is to "merge" the resources output directory with the output directory for the classes.

sourceSets {
    main.output.resourcesDir = main.java.outputDir
}

jar {
    // I've had bad experiences when "merging" output directories
    // where the jar task ends up creating duplicate entries in the JAR.
    // Use this option to combat that.
    duplicateStrategy = DuplicatesStrategy.EXCLUDE
}

第二个选项是配置运行任务来执行JAR文件而不是展开的目录(ies)。这是有效的,因为像第一个选项一样,它将类和资源组合到同一个地方,因此资源是模块的一部分。

The second option is to configure the run task to execute the JAR file rather than the exploded directory(ies). This works because, like the first option, it combines the classes and resources into the same place and thus the resources are part of the module.

run {
    dependsOn += jar
    inputs.property('moduleName', moduleName)
    doFirst {
        // add JAR file and runtime classpath. The runtime classpath includes the output of the main source set
        // so we remove that to avoid having two of the same module on the modulepath
        def modulepath = files(jar.archivePath) + (sourceSets.main.runtimeClasspath - sourceSets.main.output)
        jvmArgs = [
                '--module-path', modulepath.asPath,
                '--module', "$moduleName/$mainClassName"
        ]
        classpath = files()
    }
}

这两个选项都可用于代替在运行任务中使用 - patch-module (解释在这个答案的第一部分)。

Both of these options can be used in place of using --patch-module in the run task (explained in the first part of this answer).

作为奖励,我就是这样添加 - 我的模块化JAR的主要类属性:

As a bonus, this is how I've been adding the --main-class attribute to my modular JARs:

jar {
    inputs.property('mainClassName', mainClassName)
    doLast {
        exec {
            executable = 'jar'
            args = [
                    '--update', '--file', "$archivePath",
                    '--main-class', "$mainClassName"
            ]
        }
    }
}

这允许你使用 java -m module.name 而不是 java -m module.name/some.package.Main 。此外,如果运行任务配置为执行JAR,您可以更改:

This allows you to use java -m module.name rather than java -m module.name/some.package.Main. Also, if the run task is configured to execute the JAR you can change:

'--module', "$moduleName/$mainClassName"

收件人:

'--module', "$moduleName"

PS如果有更好的方法,请告诉我。

这篇关于资源文件放在构建Java 9模块的Gradle项目中的哪个位置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆