资源文件在构建Java 9模块的Gradle项目中位于何处?

从IDEA 2018.2.1开始,IDE从已模块化的依赖项中启动错误突出显示的程序包`不在模块图中''。我module-

info.java在项目中添加了文件并添加了必要的requires语句,但是现在在访问src/main/resources`目录中的资源文件时遇到了麻烦。

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

当我使用+ ./gradlew run./gradlew

installDist+生成的包装器脚本时,我能够读取资源文件,但是当我从IDE运行我的应用程序时,却不是。

我向 JetBrains提出了一个问题,我了解到IDEA正在使用模块路径,而Gradle在默认情况下正在使用类路径。通过将以下代码块添加到我的build.gradle,我可以使Gradle

…不能读取任何资源文件。

run {

inputs.property("moduleName", moduleName)

doFirst {

jvmArgs = [

'--module-path', classpath.asPath,

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

]

classpath = files()

}

}

我尝试export-ing我感兴趣的资源目录作为“包”,并且在编译时由于以下原因导致构建失败:

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

使用opens而不是exports出现相同的错误,尽管降级为警告。

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

Java 9应该将资源放到哪里,如何访问它们?


在继续研究问题之后,我已经对该问题进行了大量编辑。在最初的问题中,我还试图弄清楚如何在资源目录中列出文件,但是在调查过程中,我确定这是一个红色鲱鱼-

首先,因为读取资源目录仅在资源为从file:///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执行,现在尝试InputStream在run任务中读取资源目录将抛出,IOException而不是提供文件列表。(相对于JAR,它只是得到一个空值InputStream。)

回答:

在获得JPMS适当支持方面已经取得了重大进展。每晚构建的Gradle 6.4现在包含使用Java

9模块本地开发的选项。见https://github.com/gradle/gradle/issues/890#issuecomment-603289940。


Gradle和Java 9模块支持

不幸的是,从6.0.1版开始,Gradle仍然没有对Java 9模块的一流支持,这可以从《构建Java

9模块》指南中看到。

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

一些社区插件,例如java9-modularity插件,试图增加支持。本指南将在开发时更新有关如何使用内置Gradle支持的更多信息。

本指南过去更加详尽,并提供了有关如何“手动”自定义现有任务的示例。但是,此后更改为上面的建议,建议使用至少提供某些Java

9支持的第三方插件。这些社区插件中的某些似乎不仅仅提供模块支持,例如对使用jlinkGradle工具的支持。

Gradle项目有一个“史诗级”,据称可以跟踪Java

9模块支持:JPMS支持#890。


问题

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

build/

|--classes/

|--resources/

classes目录是module-

info.class文件放置。这会给模块系统带来问题,因为从技术上讲,目录下的resources文件不包含在目录中的模块内classes。当使用类路径而不是模块路径时,这不是问题,因为模块系统将整个类路径视为一个巨型模块(即所谓的

未命名模块 )。

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

“仅资源包”是指包含资源但没有资源具有.java.class扩展名的包。

当然,如果仅模块本身可以访问资源,则opens无需添加指令。仅当需要使其他模块可以访问资源时,才需要为包含资源的软件包添加此类指令,因为模块中的资源需要封装。

可以 封装 命名模块中的资源,以使其他模块中的代码无法定位该资源。确定是否可以定位资源如下:

  • 如果资源名称以“ .class” 结尾,则不会被封装。
  • 包名

    是从资源名称的。如果程序包名称是模块中的程序包,则仅当程序包至少对调用方的模块打开时,该方法的调用方才能找到资源。如果资源不在模块的包装中,则不会封装该资源。


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

回答:

最简单的选择是使用现成的Gradle插件,该插件可以为您处理所有事情。《 构建Java 9模块》

指南提供了一个这样的插件的示例,我相信它是目前最全面的插件:gradle-modules-plugin。

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()

}

}

}

上面的用法--patch-

module(请参阅java工具文档):

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

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

  • 测试代码。您必须确定测试代码是放在自己的模块中还是被修补到主代码的模块中(假设您没有将所有内容都保留在类路径中以进行单元测试)。

    • 单独的模块:可能更容易配置(大致为相同的结构compileTestJavatest作为用于compileJavarun); 但是,由于模块系统不允许拆分包,因此,这仅允许“黑盒测试”(即,您只能测试公共API)。
    • 修补模块:允许进行“白盒测试”,但较难配置。由于您没有requires用于测试依赖项的任何指令,因此必须添加适当的--add-modules--add-reads参数。然后,您必须考虑到大多数测试框架都需要反射访问。由于您不太可能将主模块作为开放模块,因此还必须添加适当的--add-opens参数。
    • 打包。一个模块可以有一个主类,所以你只需要使用--module <module-name>代替--module <module-name>/<mainclass-name>。这是通过--main-class使用jar工具指定选项来完成的。不幸的是,Jar据我所知,Gradle 任务类没有指定此方法。一种选择是使用doLastexec手动调用该jar工具和--updateJAR文件。

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

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

回答:

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

sourceSets {

main {

output.setResourcesDir(java.outputDir)

}

}

在将资源输出设置为与Java输出相同的情况下,可能有必要使用配置jar任务duplicatesStrategy =

DuplicatesStrategy.EXCLUDE

我相信,如果您希望使用opens仅资源包,则可能需要这样做。即使--patch-

module由于opens指令而在运行时也会出错,因为模块系统似乎在应用之前执行了一些完整性验证--patch-

module。换句话说,仅资源包不会很快“存在”。我不确定是否有任何插件可以处理此用例。

但是,在编译时,允许一个opens软件包不存在,尽管它javac会发出警告。话虽如此,有可能通过--patch-

modulecompileJava任务中使用来消除警告。

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很快会以一流的方式支持Java 9模块。我相信Maven在这方面要走得更远。

以上是 资源文件在构建Java 9模块的Gradle项目中位于何处? 的全部内容, 来源链接: utcz.com/qa/401276.html

回到顶部