Gradle使用基于配置的约定方法来构建基于JVM的项目,该方法借鉴了Apache Maven的几种约定. 特别是,它对源文件和资源使用相同的默认目录结构,并且可与Maven兼容的存储库一起使用.

We will look at Java projects in detail in this chapter, but most of the topics apply to other supported JVM languages as well, such as Kotlin, Groovy and Scala. If you don’t have much experience with building JVM-based projects with Gradle, take a look at the Java tutorials for step-by-step instructions on how to build various types of basic Java projects.

本节中的示例使用Java库插件. 但是,所有JVM插件都共享所描述的功能. 不同插件的详细信息可在其专用文档中找到.

Introduction

Java项目的最简单构建脚本应用Java库插件,并可以选择设置项目版本和Java兼容版本:

例子1.应用Java库插件
build.gradle
plugins {
    id 'java-library'
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

version = '1.2.1'
build.gradle.kts
plugins {
    `java-library`
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

version = "1.2.1"

通过应用Java库插件,您可以获得许多功能:

  • 一个compileJava任务,用于编译src / main / java下的所有Java源文件

  • src / test / java下源文件的compileTestJava任务

  • src / test / java运行测试的test任务

  • 一个jar任务,它将src / main / resources中main编译类和资源打包到单个名为<project>-<version> .jar的 JAR中.

  • 一个为main类生成Javadoc的javadoc任务

这还不足以构建任何重要的Java项目-至少,您可能会有一些文件依赖性. 但它意味着你的构建脚本只需要特定于项目的信息.

尽管示例中的属性是可选的,但我们建议您在项目中指定它们. 兼容性选项可以缓解使用不同Java编译器版本构建的项目的问题,并且版本字符串对于跟踪项目的进度很重要. 默认情况下,项目版本也用于归档名称中.

Java库插件还将上述任务集成到标准的基础插件生命周期任务中

  • jarassemble [ 1 ]

  • test附带check

本章的其余部分介绍了根据需要自定义构建的不同方法. 稍后,您还将看到如何调整库,应用程序,Web应用程序和企业应用程序的构建.

Declaring your source files via source sets

Gradle对Java的支持是第一个引入用于构建基于源代码的项目的新概念的方法: 源代码集 . 主要思想是源文件和资源通常按类型进行逻辑分组,例如应用程序代码,单元测试和集成测试. 每个逻辑组通常都有其自己的文件依赖项集,类路径等. 重要的是,构成源集的文件不必位于同一目录中

源集是一个强大的概念,将编译的几个方面联系在一起:

  • 源文件及其位置

  • 编译类路径,包括任何必需的依赖项(通过Gradle 配置

  • where the compiled class files are placed

您可以在此图中看到它们之间的相互关系:

java sourcesets compilation
图1.源集和Java编译

阴影框代表源集本身的属性. 最重要的是,Java库插件会为您或插件定义的每个源集(称为compile SourceSet Java )以及几个依赖项配置自动创建一个编译任务.

main来源集

大多数语言插件(包括Java)会自动创建一个名为main的源集,该源集用于项目的生产代码. 此源集的特殊之处在于,其名称不包括在配置和任务的名称中,因此为什么您只有一个compileJava任务和compileOnlyimplementation配置,而不是分别拥有compileMainJavamainCompileOnlymainImplementation .

Java项目通常包括源文件以外的资源,例如属性文件,这些资源可能需要处理(例如,通过替换文件中的标记)并打包在最终JAR中. Java库插件通过自动为每个定义的源集(称为process SourceSet Resources (或main源集的processResources ))创建专用任务来处理此问题. 下图显示了源集如何适合此任务:

java sourcesets process resources
图2.处理源集的非源文件

和以前一样,阴影框表示源集的属性,在这种情况下,它包括资源文件的位置以及将它们复制到的位置.

除了main源集之外,Java库插件还定义了一个代表项目测试的test源集. 此源集由运行测试的test任务使用. 您可以在Java测试一章中了解有关此任务和相关主题的更多信息.

项目通常使用此源集进行单元测试,但如果需要,也可以将其用于集成,验收和其他类型的测试. 另一种方法是为其他每种测试类型定义一个新的源集 ,通常是出于以下两个或多个原因:

  • 您想要使测试彼此分开以保持美观和可管理性

  • 不同的测试类型需要不同的编译或运行时类路径或设置上的其他差异

您可以在Java测试一章中看到这种方法的示例,该示例向您展示了如何在项目中设置集成测试 .

您将了解有关源集及其提供的功能的更多信息:

Managing your dependencies

绝大多数Java项目都依赖于库,因此管理项目的依赖关系是构建Java项目的重要组成部分. 依赖管理是一个大话题,因此我们将在这里重点介绍Java项目的基础知识. 如果您想深入研究细节,请查看依赖管理介绍 .

为Java项目指定依赖项仅需要三点信息:

  • 您需要哪个依赖项,例如名称和版本

  • 它需要什么,例如编译或运行

  • 在哪里寻找

前两个在dependencies {}块中指定,第三个在repositories {}块中指定. 例如,要告诉Gradle您的项目需要3.6.7版的Hibernate Core来编译和运行生产代码,并且您想从Maven Central存储库下载该库,可以使用以下片段:

例子2.声明依赖
build.gradle
repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.hibernate:hibernate-core:3.6.7.Final'
}
build.gradle.kts
repositories {
    mavenCentral()
}

dependencies {
    implementation("org.hibernate:hibernate-core:3.6.7.Final")
}

三个元素的Gradle术语如下:

  • 存储库 (例如: mavenCentral() )—在哪里查找声明为依赖项的模块

  • 配置 (例如: implementation )-命名的依赖项集合,针对特定目标分组在一起,例如编译或运行模块-一种更灵活的Maven范围形式

  • 模块坐标 (例如: org.hibernate:hibernate-core-3.6.7.Final )—依赖项的ID,通常为' <group><module><version> '(或' <groupId> : Mart术语中的<artifactId><version> ')

您可以在此处找到更全面的依赖项管理术语表.

就配置而言,主要感兴趣的是:

  • compileOnly — for dependencies that are necessary to compile your production code but shouldn’t be part of the runtime classpath

  • implementation (取代compile )—用于编译和运行时

  • runtimeOnly (取代runtime )—仅在运行时使用,而不用于编译

  • testCompileOnly —与compileOnly相同,只是用于测试

  • testImplementation —测试implementation等效项

  • testRuntimeOnly —测试等效于runtimeOnly

您可以在插件参考一章中了解有关它们的更多信息以及它们之间的关系.

请注意, Java库插件为编译模块和依赖该模块的任何模块所需的依赖项创建了一个附加配置api .

为什么没有compile配置?

Java库插件在历史上一直使用compile配置来获得编译和运行项目的生产代码所需的依赖项. 现在已弃用它,并且在使用时会发出警告,因为它无法区分影响Java库项目的公共API和不影响Java库项目的公共API的依赖项. 您可以在构建Java库中了解有关此区别的重要性的更多信息.

我们仅在此处进行了介绍,因此,一旦您熟悉使用Gradle构建Java项目的基础知识,我们建议您阅读专用的依赖管理章节. 需要进一步阅读的一些常见方案包括:

您会发现Gradle具有丰富的API用于处理依赖关系-一种需要花费时间来掌握的API,但对于常见的情况却很容易使用.

Compiling your code

如果遵循以下约定,则可以同时轻松地编译生产代码和测试代码:

  1. 将生产源代码放在src / main / java目录下

  2. 将测试源代码放在src / test / java下

  3. compileOnlyimplementation配置中声明您的生产编译依赖项(请参阅上一节)

  4. testCompileOnlytestImplementation配置中声明您的测试编译依赖testImplementation

  5. 运行compileJava生产代码和任务compileTestJava为测试

其他JVM语言插件,例如Groovy的插件,遵循相同的约定模式. 我们建议您尽可能遵循这些约定,但不必这样做. 有几种自定义选项,您将在下面看到.

Customizing file and directory locations

假设您有一个旧项目,该项目使用src目录存储生产代码并测试测试代码. 传统的目录结构不起作用,因此您需要告诉Gradle在哪里可以找到源文件. 您可以通过源集配置来实现.

每个源集定义其源代码所在的位置,以及类文件的资源和输出目录. 您可以使用以下语法覆盖约定值:

例子3.声明自定义源目录
build.gradle
sourceSets {
    main {
         java {
            srcDirs = ['src']
         }
    }

    test {
        java {
            srcDirs = ['test']
        }
    }
}
build.gradle.kts
sourceSets {
    main {
        java {
            setSrcDirs(listOf("src"))
        }
    }

    test {
        java {
            setSrcDirs(listOf("test"))
        }
    }
}

现在Gradle将只在src中直接搜索并测试相应的源代码. 如果您不想覆盖约定,而只想添加一个额外的源目录,该目录可能包含一些您想分开的第三方源代码,该怎么办? 语法类似:

例子4.附加地声明自定义源目录
build.gradle
sourceSets {
    main {
        java {
            srcDir 'thirdParty/src/main/java'
        }
    }
}
build.gradle.kts
sourceSets {
    main {
        java {
            srcDir("thirdParty/src/main/java")
        }
    }
}

至关重要的是,我们在这里使用方法 srcDir()追加目录路径,而设置srcDirs属性将替换所有现有值. 这是Gradle中的常见约定:设置属性将替换值,而相应的方法将附加值.

您可以在DSL参考中的SourceSetSourceDirectorySet上查看源集上可用的所有属性和方法. 请注意, srcDirssrcDir()都在SourceDirectorySet .

Changing compiler options

大多数编译器选项可通过相应的任务访问,例如compileJavacompileTestJava . 这些任务的类型为JavaCompile ,因此,请阅读任务参考以获取最新,最全面的选项列表.

例如,如果要为编译器使用单独的JVM进程并防止编译失败使构建失败,则可以使用以下配置:

例子5.设置Java编译器选项
build.gradle
compileJava {
    options.incremental = true
    options.fork = true
    options.failOnError = false
}
build.gradle.kts
tasks.compileJava {
    options.isIncremental = true
    options.isFork = true
    options.isFailOnError = false
}

这也是您可以更改编译器的详细程度,禁用字节码中的调试输出以及配置编译器可以在何处找到注释处理器的方式.

在项目级别定义了Java编译器的两个常用选项:

sourceCompatibility

定义应将源文件视为Java的语言版本.

targetCompatibility

定义您的代码应在其上运行的最低JVM版本,即,它确定编译器生成的字节码的版本.

如果出于任何原因需要或想要多个编译任务,则可以创建一个新的源集 ,也可以简单地定义一个JavaCompile类型的新任务. 接下来,我们来看设置新的源集.

Compiling and testing Java 6/7

Gradle只能在Java版本8或更高版本上运行.

Gradle仍然支持Java 6和Java 7的编译,测试,生成Javadoc并执行应用程序.不支持Java 5.

要使用Java 6或Java 7,需要配置以下任务:

  • JavaCompile任务派生并使用正确的Java主页

  • Javadoc任务以使用正确的javadoc可执行文件

  • Test JavaExec任务以使用正确的java可执行文件.

以下示例显示了如何调整build.gradle . 为了能够独立于构建机器,应在每台开发人员机器的用户主目录中的GRADLE_USER_HOME/gradle.properties [ 2 ]中配置旧Java主目录和目标版本的位置,如示例所示.

Example: Configure Java 7 build

gradle.properties
# in $HOME/.gradle/gradle.properties
javaHome=/Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home
targetJavaVersion=1.7
build.gradle
assert hasProperty('javaHome'): "Set the property 'javaHome' in your your gradle.properties pointing to a Java 6 or 7 installation"
assert hasProperty('targetJavaVersion'): "Set the property 'targetJavaVersion' in your your gradle.properties to '1.6' or '1.7'"

java {
    sourceCompatibility = JavaVersion.toVersion(targetJavaVersion)
}

def javaExecutablesPath = new File(javaHome, 'bin')
def javaExecutables = [:].withDefault { execName ->
    def executable = new File(javaExecutablesPath, execName)
    assert executable.exists(): "There is no ${execName} executable in ${javaExecutablesPath}"
    executable
}
tasks.withType(AbstractCompile) {
    options.with {
        fork = true
        forkOptions.javaHome = file(javaHome)
    }
}
tasks.withType(Javadoc) {
    executable = javaExecutables.javadoc
}
tasks.withType(Test) {
    executable = javaExecutables.java
}
tasks.withType(JavaExec) {
    executable = javaExecutables.java
}
build.gradle.kts
require(hasProperty("javaHome")) { "Set the property 'javaHome' in your your gradle.properties pointing to a Java 6 or 7 installation" }
require(hasProperty("targetJavaVersion")) { "Set the property 'targetJavaVersion' in your your gradle.properties to '1.6' or '1.7'" }

val javaHome: String by project
val targetJavaVersion: String by project

java {
    sourceCompatibility = JavaVersion.toVersion(targetJavaVersion)
}

val javaExecutablesPath = File(javaHome, "bin")
fun javaExecutable(execName: String): String {
    val executable = File(javaExecutablesPath, execName)
    require(executable.exists()) { "There is no ${execName} executable in ${javaExecutablesPath}" }
    return executable.toString()
}
tasks.withType<JavaCompile>().configureEach {
    options.apply {
        isFork = true
        forkOptions.javaHome = file(javaHome)
    }
}
tasks.withType<Javadoc>().configureEach {
    executable = javaExecutable("javadoc")
}
tasks.withType<Test>().configureEach {
    executable = javaExecutable("java")
}
tasks.withType<JavaExec>().configureEach {
    executable = javaExecutable("java")
}

Compiling independent sources separately

大多数项目至少有两个独立的源集:生产代码和测试代码. Gradle已经将此场景作为其Java约定的一部分,但是如果您有其他来源的话该怎么办? 最常见的情况之一是当您进行某种形式或其他形式的单独集成测试时. 在这种情况下,自定义源集可能正是您所需要的.

您可以在Java测试一章中看到设置集成测试的完整示例. 您可以设置以相同方式担当不同角色的其他源集. 问题就变成了:什么时候应该定义自定义源集?

要回答该问题,请考虑以下来源:

  1. 需要使用唯一的类路径进行编译

  2. 生成与maintest类的处理方式不同的类

  3. 构成项目的自然组成部分

如果您对3和其他任何一个的回答都是肯定的,那么自定义源集可能是正确的方法. 例如,集成测试通常是项目的一部分,因为它们测试main的代码. 此外,它们通常具有独立于test源集的独立性,或者需要与自定义Test任务一起运行.

其他常见方案不太明确,可能有更好的解决方案. 例如:

  • Separate API and implementation JARs — it may make sense to have these as separate projects, particularly if you already have a multi-project build

  • 生成的源-如果生成的源应使用生产代码进行编译,则将其路径添加到main源集中,并确保compileJava任务依赖于生成源的任务

如果不确定是否要创建自定义源集,请继续进行操作. 它应该很简单,如果不是,则可能不是适合该工作的工具.

Managing resources

Many Java projects make use of resources beyond source files, such as images, configuration files and localization data. Sometimes these files simply need to be packaged unchanged and sometimes they need to be processed as template files or in some other way. Either way, the Java Library Plugin adds a specific Copy task for each source set that handles the processing of its associated resources.

任务的名称遵循process SourceSet Resources的约定(或main源集的processResources ,它将自动将src / [sourceSet] / resources中的所有文件复制到将包含在生产JAR中的目录中. 该目标目录也将包含在测试的运行时类路径中.

由于processResources是" Copy任务的实例,因此您可以执行" 使用文件"一章中描述的任何处理.

Java properties files and reproducible builds

您可以通过WriteProperties任务轻松创建Java属性文件,该任务解决了Properties.store()一个众所周知的问题,该问题可能会减少增量构建的用处.

即使使用相同的属性和值,用于编写属性文件的标准Java API也会每次生成一个唯一的文件,因为注释中包括了时间戳. 如果未更改任何属性,则Gradle的WriteProperties任务逐字节生成完全相同的输出. 这是通过对属性文件的生成方式进行一些调整来实现的:

  • 没有时间戳注释添加到输出

  • 行分隔符与系统无关,但是可以显式配置(默认为'\n'

  • 属性按字母顺序排序

有时可能需要在不同的计算机上以字节为单位重新创建归档. 您要确保从源代码构建工件,无论在何时何地构建,都逐字节产生相同的结果. 这对于诸如reproducible-builds.org之类的项目是必需的.

这些调整不仅可以导致更好的增量构建集成,而且还有助于可复制的构建 . 本质上,可重现的构建可确保您无论在何时何地在什么系统上运行,都可以从构建执行中看到相同的结果,包括测试结果和生产二进制文件.

Running tests

除了在src / test / java中提供单元测试的自动编译功能之外,Java库插件还对运行使用JUnit 3、4和5的测试( Gradle 4.6中提供了对 JUnit 5的支持)和TestNG的本地支持. 你得到:

  • 使用test源集的Test类型的自动test任务

  • HTML测试报告,其中包含运行的所有 Test任务的结果

  • 轻松过滤要运行的测试

  • 精细控制测试的运行方式

  • 有机会创建自己的测试执行和测试报告任务

不会为声明的每个源集获得一个Test任务,因为不是每个源集都代表测试! 这就是为什么您通常需要为集成和验收测试之类的东西创建自己的Test任务 ,如果它们不能包含在test源集中.

由于涉及测试的内容很多,因此该主题有其自己的章节 ,我们在其中进行介绍:

  • 测试如何运行

  • 如何通过过滤运行测试的子集

  • Gradle如何发现测试

  • 如何配置测试报告并添加自己的报告任务

  • 如何利用特定的JUnit和TestNG功能

您还可以在DSL参考中的Test上了解有关配置测试的更多信息.

Packaging and publishing

如何打包和潜在地发布Java项目取决于它是什么类型的项目. 库,应用程序,Web应用程序和企业应用程序都有不同的要求. 在本节中,我们将重点介绍Java库插件提供的基础知识.

默认情况下,Java库插件提供了jar任务,该任务将所有已编译的生产类和资源打包到一个JAR中. 该JAR也是由assemble任务自动构建的. 此外,如果需要,可以将插件配置为提供javadocJarsourcesJar任务,以打包Javadoc和源代码. 如果使用发布插件,这些任务将在发布期间自动运行或可以直接调用.

示例6.配置一个项目以发布Javadoc和源
build.gradle
java {
    withJavadocJar()
    withSourcesJar()
}
build.gradle.kts
java {
    withJavadocJar()
    withSourcesJar()
}

如果要创建"超级"(又称"胖")JAR,则可以使用如下任务定义:

例子7.创建一个Java uber或fat JAR
build.gradle
plugins {
    id 'java'
}

version = '1.0.0'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'commons-io:commons-io:2.6'
}

task uberJar(type: Jar) {
    archiveClassifier = 'uber'

    from sourceSets.main.output

    dependsOn configurations.runtimeClasspath
    from {
        configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) }
    }
}
build.gradle.kts
plugins {
    java
}

version = "1.0.0"

repositories {
    mavenCentral()
}

dependencies {
    implementation("commons-io:commons-io:2.6")
}

tasks.register<Jar>("uberJar") {
    archiveClassifier.set("uber")

    from(sourceSets.main.get().output)

    dependsOn(configurations.runtimeClasspath)
    from({
        configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) }
    })
}

See Jar for more details on the configuration options available to you. And note that you need to use archiveClassifier rather than archiveAppendix here for correct publication of the JAR.

您可以使用发布插件之一来发布由Java项目创建的JAR:

Modifying the JAR manifest

JarWarEar任务的每个实例都有一个manifest属性,该属性允许您自定义进入相应归档文件的MANIFEST.MF文件. 下面的示例演示如何在JAR清单中设置属性:

例子8.自定义MANIFEST.MF
build.gradle
jar {
    manifest {
        attributes("Implementation-Title": "Gradle",
                   "Implementation-Version": archiveVersion)
    }
}
build.gradle.kts
tasks.jar {
    manifest {
        attributes(
            "Implementation-Title" to "Gradle",
            "Implementation-Version" to archiveVersion
        )
    }
}

请参阅清单以获取其提供的配置选项.

您还可以创建Manifest独立实例. 这样做的原因之一是在JAR之间共享清单信息. 下面的示例演示如何在JAR之间共享通用属性:

示例9.创建清单对象.
build.gradle
ext.sharedManifest = manifest {
    attributes("Implementation-Title": "Gradle",
               "Implementation-Version": version)
}
task fooJar(type: Jar) {
    manifest = project.manifest {
        from sharedManifest
    }
}
build.gradle.kts
val sharedManifest = the<JavaPluginConvention>().manifest {
    attributes (
        "Implementation-Title" to "Gradle",
        "Implementation-Version" to version
    )
}

tasks.register<Jar>("fooJar") {
    manifest = project.the<JavaPluginConvention>().manifest {
        from(sharedManifest)
    }
}

您可以使用的另一种选择是将清单合并到单个Manifest对象中. 这些源清单可以采用文本的形式,也可以采用另一个Manifest对象的形式. 在以下示例中,源清单是所有文本文件,但sharedManifest除外,后者是上一示例中的Manifest对象:

例子10.单独的MANIFEST.MF用于一个特定的档案
build.gradle
task barJar(type: Jar) {
    manifest {
        attributes key1: 'value1'
        from sharedManifest, 'src/config/basemanifest.txt'
        from(['src/config/javabasemanifest.txt', 'src/config/libbasemanifest.txt']) {
            eachEntry { details ->
                if (details.baseValue != details.mergeValue) {
                    details.value = baseValue
                }
                if (details.key == 'foo') {
                    details.exclude()
                }
            }
        }
    }
}
build.gradle.kts
tasks.register<Jar>("barJar") {
    manifest {
        attributes("key1" to "value1")
        from(sharedManifest, "src/config/basemanifest.txt")
        from(listOf("src/config/javabasemanifest.txt", "src/config/libbasemanifest.txt")) {
            eachEntry(Action<ManifestMergeDetails> {
                if (baseValue != mergeValue) {
                    value = baseValue
                }
                if (key == "foo") {
                    exclude()
                }
            })
        }
    }
}

清单按照在from语句中声明的顺序合并. 如果基本清单和合并清单都定义了相同键的值,则默认情况下合并清单将获胜. 您可以通过添加eachEntry动作来完全自定义合并行为,在其中您可以访问结果清单的每个条目的ManifestMergeDetails实例. 请注意,在生成JAR时或在调用Manifest.writeTo()Manifest.getEffectiveManifest()时,合并都是延迟进行的.

说到writeTo() ,您可以使用它轻松地随时将清单写入磁盘,如下所示:

例子11.将一个MANIFEST.MF保存到磁盘
build.gradle
jar.manifest.writeTo("$buildDir/mymanifest.mf")
build.gradle.kts
tasks.named<Jar>("jar") { manifest.writeTo("$buildDir/mymanifest.mf") }

Generating API documentation

Java库插件提供javadoc类型的任务的Javadoc ,将生成标准的Javadoc文档所有的生产代码,即无论来源是main来源集. 该任务支持Javadoc参考文档中描述的核心Javadoc和标准doclet选项. 有关这些选项的完整列表,请参见CoreJavadocOptionsStandardJavadocDocletOptions .

作为您可以做的事的一个例子,想象一下您想在Javadoc注释中使用Asciidoc语法. 为此,您需要将Asciidoclet添加到Javadoc的doclet路径. 这是一个执行此操作的示例:

示例12.将自定义doclet与Javadoc一起使用
build.gradle
configurations {
    asciidoclet
}

dependencies {
    asciidoclet 'org.asciidoctor:asciidoclet:1.+'
}

task configureJavadoc {
    doLast {
        javadoc {
            options.doclet = 'org.asciidoctor.Asciidoclet'
            options.docletpath = configurations.asciidoclet.files.toList()
        }
    }
}

javadoc {
    dependsOn configureJavadoc
}
build.gradle.kts
val asciidoclet by configurations.creating

dependencies {
    asciidoclet("org.asciidoctor:asciidoclet:1.+")
}

tasks.register("configureJavadoc") {
    doLast {
        tasks.javadoc {
            options.doclet = "org.asciidoctor.Asciidoclet"
            options.docletpath = asciidoclet.files.toList()
        }
    }
}

tasks.javadoc {
    dependsOn("configureJavadoc")
}

您不必为此创建配置,但这是一种处理独特目的所需依赖项的绝妙方法.

您可能还想创建自己的Javadoc任务,例如为测试生成API文档:

例子13.定义一个自定义Javadoc任务
build.gradle
task testJavadoc(type: Javadoc) {
    source = sourceSets.test.allJava
}
build.gradle.kts
tasks.register<Javadoc>("testJavadoc") {
    source = sourceSets.test.get().allJava
}

这些只是您可能会遇到的两个不重要但常见的自定义.

Cleaning the build

Java库插件通过应用基本插件clean任务添加到您的项目中. 此任务只是删除$buildDir目录中的所有内容,因此为什么要始终将构建生成的文件放在其中. 该任务是Delete的一个实例,您可以通过设置其dir属性来更改其删除的dir .

Building JVM components

所有特定的JVM插件都构建在Java插件之上. 上面的示例仅说明了此基本插件提供的概念,并与所有JVM插件共享.

继续阅读以了解哪些插件适合哪种项目类型,因为建议选择特定的插件而不是直接应用Java插件.

Building Java libraries

库项目的独特之处在于它们被其他Java项目使用(或"消耗"). 这意味着与JAR文件一起发布的依赖项元数据(通常以Maven POM的形式)至关重要. 特别是,库的使用者应能够区分两种不同类型的依赖关系:仅依赖于编译库的依赖关系和也依赖于编译使用者的依赖关系.

Gradle通过Java库插件来管理这一区别,该插件除了本章介绍的实现之外,还引入了api配置. 如果依赖项的类型出现在库的公共类的公共字段或方法中,则该依赖项通过库的公共API公开,因此应将其添加到api配置中. 否则,依赖项是内部实现细节,应将其添加到Implementation中 .

如果不确定API和实现依赖项之间的区别,请在Java库插件一章中进行详细说明. 此外,您可以在相应的指南中看到构建Java库的基本,实际示例.

Building Java applications

没有将打包为JAR的Java应用程序设置为易于从命令行或桌面环境启动. 应用程序插件通过创建一个包含生产JAR,其依赖项以及Unix-like和Windows系统启动脚本的发行版来解决命令行方面的问题.

有关更多详细信息,请参见插件的章节,但这是您所获得的快速摘要:

  • assemble创建应用程序的ZIP和TAR发行版,其中包含运行它所需的一切

  • 一个从构建中启动应用程序的run任务(以便于测试)

  • Shell和Windows Batch脚本启动应用程序

您可以在相应的指南中看到构建Java应用程序的基本示例.

Building Java web applications

Java Web应用程序可以根据您使用的技术以多种方式打包和部署. 例如,您可以将Spring Boot与胖JAR或Netty上运行的基于Reactive的系统一起使用. 无论您使用什么技术,Gradle及其庞大的插件社区都可以满足您的需求. 但是,Core Gradle仅直接支持部署为WAR文件的传统基于Servlet的Web应用程序.

该支持来自War插件 ,该插件会自动应用Java插件并添加一个额外的打包步骤,该步骤执行以下操作:

  • 将静态资源从src / main / webapp复制到WAR的根目录中

  • 将编译后的生产类复制到WAR的WEB-INF / classes子目录中

  • 将库依赖项复制到WAR的WEB-INF / lib子目录中

这是由war任务完成的, war任务有效地替换了jar任务(尽管该任务仍然存在),并附加到了assemble生命周期任务. 有关更多详细信息和配置选项,请参见插件的章节.

没有直接从内部版本运行Web应用程序的核心支持,但是我们建议您尝试使用Gretty社区插件,该插件提供了嵌入式Servlet容器.

Building Java EE applications

多年来,Java企业系统已经发生了很大的变化,但是如果您仍要部署到JEE应用服务器,则可以使用Ear Plugin . 这增加了约定和构建EAR文件的任务. 插件的章节有更多详细信息.

Building Java Platforms

Java平台代表了一组依赖项声明和约束,这些声明和约束形成了要在消费项目上应用的内聚单元. 该平台没有来源,也没有自己的工件. 它在Maven世界中映射到BOM .

该支持来自Java Platform插件 ,该插件设置了不同的配置和发布组件.

该插件是例外,因为它不应用Java插件.

Building other JVM language projects

如果要利用JVM的多语言方面,此处描述的大多数内容仍然适用.

Gradle本身提供了GroovyScala插件. 请注意,可以通过将它们与java-library插件结合来进一步增强它们.

除了核心Gradle之外,还有其他很棒的插件可用于更多JVM语言!


1 . 实际上,添加到archives配置中的任何工件都将通过assemble来构建
2 . 有关gradle.properties更多详细信息,请参阅Gradle配置属性.