编程语言
首页 > 编程语言> > 资源文件放在构建Java 9模块的Gradle项目中的哪个位置?

资源文件放在构建Java 9模块的Gradle项目中的哪个位置?

作者:互联网

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

(有关完整示例,请参阅this GitHub project.)

当我使用./gradlew run或./gradlew installDist时产生的结果
包装脚本,我能够读取资源文件,但当我运行我的
来自IDE的app,我不是.

我提交了issue
与JetBrains一起,我学到的是IDEA正在使用该模块
path,而Gradle默认使用classpath.通过增加
以下块到我的build.gradle,我得到了Gradle
还…无法读取任何资源文件.

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

我尝试将我感兴趣的资源目录导出为
“package”,并在编译时遇到构建失败:

error: package is empty or does not exist: mydir

虽然降级了,但使用opens而不是export会出现同样的错误
发出警告

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

应该在Java 9中使用哪些资源,以及如何访问它们?

注意:继续之后,我已经大大编辑了这个问题
研究这个问题.在最初的问题中,我也在尝试
弄清楚如何列出资源目录中的文件,但是在
调查过程我确定这是一个红鲱鱼 –
首先,因为只读取资源目录时才有效
正在从file:/// URL读取资源(甚至可能不会)
第二,因为普通文件也没有工作,所以很清楚
问题是一般的资源文件,而不是具体的
目录.

解:

根据Slaw’s answer,我在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,而不是提供文件列表. (对于JAR,它只是获取一个空的InputStream.)

解决方法:

Gradle’s epic关于Jigsaw支持我已经了解了一个可以简化下面描述的过程的插件:gradle-modules-plugin.史诗还提到了其他插件,如chainsaw(这是实验拼图插件的一个分支).不幸的是,我还没有尝试过任何一个,所以我不能评论他们如何处理资源,如果有的话.

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

但是,您mention这是关于从模块内(即不是从外部模块)访问资源.使用简单的build.gradle配置解决这个问题应该不会太难.

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

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

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

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

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

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

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

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

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

这就是我一直在做的事情,到目前为止它已经很好了.

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

如果要允许外部模块访问资源,则模块必须打开(参见Eng.Fouad’s answer)资源的包至少读取模块(这仅适用于encapsulated资源).但是,正如您所发现的,这会导致编译警告和运行时错误.

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

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

>运行时错误是因为模块系统找不到您声明为open指令的包.

>再次,假设只有资源的包.
>即使使用上面提到的–patch-module选项,也会发生这种情况.我想模块系统在应用补丁之前会进行一些完整性检查.

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

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

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

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

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文件而不是展开的目录.这是有效的,因为像第一个选项一样,它将类和资源组合到同一个地方,因此资源是模块的一部分.

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(在本答案的第一部分中进行了解释).

作为奖励,这就是我将-main-class属性添加到模块化JAR中的方式:

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,您可以更改:

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

至:

'--module', "$moduleName"

附:如果有更好的方法,请告诉我.

标签:java-module,java,gradle,embedded-resource,java-9
来源: https://codeday.me/bug/20190926/1818643.html