当前位置: 首页 > 知识库问答 >
问题:

在构建Java9模块的Gradle项目中,资源文件放在哪里?

空翼
2023-03-14

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

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

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

我向JetBrains提交了一个问题,我了解到IDEA正在使用模块路径,而Gradle在默认情况下正在使用类路径。通过将以下块添加到我的build.gradle,我能够让Gradle也...无法读取任何资源文件。

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

我尝试将我感兴趣的资源目录作为一个“包”导出,但在编译时,由于以下原因导致生成失败:

错误:包为空或不存在:mydir

使用打开而不是导出会得到相同的错误,尽管降级为警告。

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

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

注意:在继续研究这个问题之后,我对这个问题进行了大量的编辑。在最初的问题中,我还试图弄清楚如何列出资源目录中的文件,但在调查过程中,我确定这是一个转移视线的问题——首先,因为只有从文件://URL读取资源时,才可以读取资源目录(甚至可能不在此时),第二个原因是普通文件也不起作用,所以很明显问题出在一般的资源文件上,而不是目录上。

解决方案:

根据Slaw的回答,我在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的代码,使run任务对JAR执行,那么尝试读取run任务中的资源目录InputStream会抛出IOException,而不是提供文件列表。(在JAR上,它只得到一个空的InputStream

共有2个答案

柴正祥
2023-03-14

除了@Slaw的答案(感谢他),我还必须打开包含调用方模块资源的包。如下(moduleone.name module info.java):

opens io.fouad.packageone to moduletwo.name;

否则,以下内容将返回null

A.class.getResource("/io/fouad/packageone/logging.properties");

考虑到类A在模块modulewo中。名称和文件日志记录。属性位于模块模块内。名称

或者,moduleone.name可以公开返回资源的实用程序方法:

public static URL getLoggingConfigFileAsResource()
{
    return A.class.getResource("/io/fouad/packageone/logging.properties");
}

凌通
2023-03-14

更新(2020年3月25日):在适当的JPMS支持方面取得了重大进展。Gradle 6.4的夜间构建现在包括使用Java9模块进行本地开发的选项。看见https://github.com/gradle/gradle/issues/890#issuecomment-603289940 .

更新(2020年9月29日):自Gradle 6.4(当前版本为6.6.1)以来,您现在可以在Gradle项目中本地支持JPMS模块,但您必须显式激活此功能:

java {
    modularity.inferModulePath.set(true)
}

有关更多信息,请参阅Gradle的Java模块示例,该示例还链接到各种其他相关留档。

不幸的是,Gradle仍然是6.0版的。正如《构建Java 9模块指南》所示,1-1没有对Java 9模块的一流支持。

Java9最令人兴奋的特性之一是它支持开发和部署模块化Java软件。Gradle还没有对Java9模块的一流支持。

一些社区插件,如java9模块化插件,试图添加支持。本指南将在开发时更新更多关于如何使用内置Gradle支持的信息。

注意:本指南过去更为广泛,并提供了有关如何“手动”自定义现有任务的示例。但是,它后来改为上面的内容,建议使用至少提供一些Java9支持的第三方插件。其中一些社区插件似乎不仅仅提供模块支持,例如支持使用Gradle提供的jlink工具

Gradle项目有一个“史诗”,据说跟踪Java9个模块支持: JPMS支持#890。

找不到资源文件的原因是,默认情况下,Gradle会在不同的目录中输出已编译的类和已处理的资源。它看起来像这样:

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

class目录是放置module-info.class文件的地方。这给模块系统带来了一个问题,因为从技术上讲,资源目录下的文件不包括在目录中的模块中。当使用类路径而不是模块路径时,这不是问题,因为模块系统将整个类路径视为一个巨大的模块(即所谓的未命名模块)。

如果为仅限资源的包添加opens指令,则在运行时会出现错误。错误的原因是由于前面提到的目录布局,模块中不存在包。您在编译时收到警告的原因基本相同;该模块存在于src/main/java中,src/main/resources下的资源文件在技术上不包括在该模块中。

注意:所谓的仅资源包,我指的是包含资源的包,但没有一个资源具有. java. class扩展名。

当然,如果资源仅可供模块本身访问,则无需添加opens指令。当其他模块需要访问资源时,您只需要为包含资源的包添加这样的指令,因为模块中的资源需要封装。

可以封装命名模块中的资源,以便其他模块中的代码无法定位该资源。是否可以定位资源取决于以下内容:

  • 如果资源名称以“.class”结尾,则不会对其进行封装

最终解决方案是确保资源被视为模块的一部分。然而,有几种方法可以做到这一点。

最简单的选择是使用现成的Gradle插件,它可以为您处理所有事情。《构建Java9模块指南》给出了这样一个插件的示例,我认为这是目前最全面的:gradle模块插件。

plugins {
    id("org.javamodularity.moduleplugin") version "..."
}

您还可以查看其他可用的插件。

另一个选项是配置每个所需的Gradle任务,以指定一些JVM选项。由于您主要关心的是从模块内访问资源,因此需要配置run任务,以使用资源目录修补模块。下面是一个示例(Kotlin DSL):

plugins {
    application
}

group = "..."
version = "..."

java {
    sourceCompatibility = JavaVersion.VERSION_13
}

application {
    mainClassName = "<module-name>/<mainclass-name>"
}

tasks {
    compileJava {
        doFirst {
            options.compilerArgs = listOf(
                    "--module-path", classpath.asPath,
                    "--module-version", "${project.version}"
            )
            classpath = files()
        }
    }

    named<JavaExec>("run") {
        doFirst {
            val main by sourceSets
            jvmArgs = listOf(
                    "--module-path", classpath.asPath,
                    "--patch-module", "<module-name>=${main.output.resourcesDir}",
                    "--module", application.mainClassName
            )
            classpath = files()
        }
    }
}

上面使用--patk-模块(参见java工具留档):

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

如果您使用上面的示例,它将获得一个简单的Gradle项目在模块路径上运行。不幸的是,你考虑得越多,事情就越复杂:

>

  • 单独的模块:可能更容易配置(对于compileTestJavatest的配置与compileJavarun的配置大致相同);但是,这只允许“黑盒测试”,因为模块系统不允许拆分包(即,您只能测试公共API)

包装材料模块可以有一个主类,因此您只需使用--module

应用程序插件还添加了创建启动脚本的任务(例如批处理文件)。假设您需要这些脚本,则必须将其配置为使用modulepath而不是类路径。

基本上,我强烈建议使用插件。

第三个选项是将已处理的资源配置为与已编译类具有相同的输出目录。

sourceSets {
    main {
        output.setResourcesDir(java.outputDir)
    }
}

注意:可能需要使用duplicatesStrategy=duplicatesStrategy配置jar任务。将资源输出设置为与Java输出相同时排除

我相信,如果您希望打开仅限资源的软件包,则可能需要这样做。即使使用--patch module,由于打开指令,您也会在运行时收到错误,因为模块系统似乎在应用--patch module之前执行了一些完整性验证。换句话说,仅限资源的包不会“很快”存在。我不确定是否有插件处理这个用例。

然而,在编译时,允许打开的包不存在,尽管javac会发出警告。也就是说,在compileJava任务中使用--patch module可以消除警告。

tasks.compileJava {
    doFirst {
        val main by sourceSets
        options.compilerArgs = listOf(
                "--module-path", classpath.asPath,
                "--patch-module", "<module-name>=${main.resources.sourceDirectories.asPath}"
                "--module-version", "${project.version}"
        )
        classpath = files()
    }
}

将资源和类合并到同一个地方的另一种方法是将run任务配置为针对由jar任务构建的JAR文件执行。

希望Gradle不久将以一流的方式支持Java9模块。我相信马文在这方面走得更远。

 类似资料:
  • 问题内容: 从IDEA 2018.2.1开始,IDE从已模块化的依赖项中启动错误突出显示的程序包module- info.javarequiressrc/main/resources`目录中的资源文件时遇到了麻烦。 (有关完整示例,请参阅此GitHub项目。) 当我使用+ 或+生成的包装器脚本时,我能够读取资源文件,但是当我从IDE运行我的应用程序时,却不是。 我向 JetBrains提出了一个问

  • 不管怎样,现在这个构建由于这个错误而失败,我不知道如何解决它: 错误:package some.package.impl不存在 导入一些。package.impl.roundaboutimpl; 提前感谢!

  • 资源模块 资源模块指的是图片、样式、html片段等非脚本模块(在 coolie 的世界里,一切皆是模块)。 使用方法: require('style.css', 'css'); 详细阅读点这里。 demo 初始化目录结构 新建coolie-demo6目录: . └── src 1 directory, 0 files 初始化文件 准备一张图片coolie.png,放在 src 目录下。 然后

  • 主要内容:1- 介绍,2- 示例模型,3-创建项目数学库,4- 创建另一个项目:MathWebApp,5- 创建GradleMain项目,6- 构建项目,7- 运行MathWebApp1- 介绍 本教程文章基于: Eclipse Java EE IDE for Web Developers. Version: Mars.1 Release (4.5.1) 如果您是初学者Gradle的。那么建议先看看初学者Gradle(Gradle Hello world)的文章: http://www.yiib

  • 我有一个简单的gradle项目,我想建立在Docker容器内 我有一个多级码头建设。第一阶段只是从github克隆项目。第二阶段必须建立项目,所以我可以在最后阶段运行它。但是它失败了 命令<代码>。/gradlew build - stacktrace无法执行并失败,错误为: 和stacktraces: 为什么docker用户没有在工作目录下创建文件的权限,如何添加这些权限?简单的< code >

  • 这是用Gradle构建Kotlin+Java9项目的后续。在链接后使用Groovy的Gradle。在我的情况下,使用Kotlin DSL。 基本上我有一个梯度项目与以下结构(只有相关的内容在这里): 问题:如何让链接的post(或任何其他解决方案)中提供的解决方案运行,以便使用Java9/10环境编译带有Gradle的Kotlin DSL的Kotlin项目?