您可以在支持Gradle的IDE中打开此示例。

为了让您的软件项目能够增长,您可以将 Gradle 项目组织成多个子项目,以实现所构建软件的模块化。在本指南中,您将学习如何以 Groovy 应用程序为例来构建这样的项目。但是,一般概念适用于您使用 Gradle 构建的任何软件。您可以按照本指南的步骤从头开始创建一个新项目,或者使用上面的链接下载完整的示例项目。

您将构建什么

您将构建一个由一个应用程序和多个项目组成的 Groovy 应用程序。

您需要什么

创建项目文件夹

Gradle 带有一个内置任务 init,用于在空文件夹中初始化一个新的 Gradle 项目。init 任务使用(也内置的)wrapper 任务来创建一个 Gradle wrapper 脚本 gradlew

第一步是为新项目创建一个文件夹并进入该文件夹。

$ mkdir demo
$ cd demo

运行 init 任务

在新项目目录中,在终端中使用以下命令运行 `init` 任务:`gradle init`。当提示时,选择 `1: application` 项目类型和 `3: Groovy` 作为实现语言。然后,选择 `2: Application and library project`。接下来您可以选择用于编写构建脚本的 DSL - `1 : Kotlin` 或 `2: Groovy`。对于其他问题,按 Enter 键使用默认值。

输出将如下所示

$ gradle init

Select type of build to generate:
  1: Application
  2: Library
  3: Gradle plugin
  4: Basic (build structure only)
Enter selection (default: Application) [1..4] 1

Select implementation language:
  1: Java
  2: Kotlin
  3: Groovy
  4: Scala
  5: C++
  6: Swift
Enter selection (default: Java) [1..6]  3

Project name (default: demo):

Enter target Java version (min: 7, default: 21):

Select application structure:
  1: Single application project
  2: Application and library project
Enter selection (default: Single application project) [1..2] 2

Select build script DSL:
  1: Kotlin
  2: Groovy
Enter selection (default: Kotlin) [1..2]

Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no]

BUILD SUCCESSFUL
1 actionable task: 1 executed

init 任务生成的新项目结构如下

├── gradle (1)
│   ├── libs.versions.toml (2)
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew (3)
├── gradlew.bat (3)
├── settings.gradle.kts (4)
├── buildSrc
│   ├── build.gradle.kts (5)
│   ├── settings.gradle.kts (5)
│   └── src
│       └── main
│           └── kotlin (6)
│               ├── buildlogic.groovy-application-conventions.gradle.kts
│               ├── buildlogic.groovy-common-conventions.gradle.kts
│               └── buildlogic.groovy-library-conventions.gradle.kts
├── app
│   ├── build.gradle.kts (7)
│   └── src
│       ├── main (8)
│       │   └── java
│       │       └── demo
│       │           └── app
│       │               ├── App.java
│       │               └── MessageUtils.groovy
│       └── test (9)
│           └── java
│               └── demo
│                   └── app
│                       └── MessageUtilsTest.groovy
├── list
│   ├── build.gradle.kts (7)
│   └── src
│       ├── main (8)
│       │   └── java
│       │       └── demo
│       │           └── list
│       │               └── LinkedList.groovy
│       └── test (9)
│           └── java
│               └── demo
│                   └── list
│                       └── LinkedListTest.groovy
└── utilities
    ├── build.gradle.kts (7)
    └── src
        └── main (8)
            └── java
                └── demo
                    └── utilities
                        ├── JoinUtils.groovy
                        ├── SplitUtils.groovy
                        └── StringUtils.groovy
├── gradle (1)
│   ├── libs.versions.toml (2)
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew (3)
├── gradlew.bat (3)
├── settings.gradle (4)
├── buildSrc
│   ├── build.gradle (5)
│   ├── settings.gradle (5)
│   └── src
│       └── main
│           └── groovy (6)
│               ├── buildlogic.groovy-application-conventions.gradle
│               ├── buildlogic.groovy-common-conventions.gradle
│               └── buildlogic.groovy-library-conventions.gradle
├── app
│   ├── build.gradle (7)
│   └── src
│       ├── main (8)
│       │   └── java
│       │       └── demo
│       │           └── app
│       │               ├── App.java
│       │               └── MessageUtils.java
│       └── test (9)
│           └── java
│               └── demo
│                   └── app
│                       └── MessageUtilsTest.java
├── list
│   ├── build.gradle (7)
│   └── src
│       ├── main (8)
│       │   └── java
│       │       └── demo
│       │           └── list
│       │               └── LinkedList.java
│       └── test (9)
│           └── java
│               └── demo
│                   └── list
│                       └── LinkedListTest.java
└── utilities
    ├── build.gradle (7)
    └── src
        └── main (8)
            └── java
                └── demo
                    └── utilities
                        ├── JoinUtils.java
                        ├── SplitUtils.java
                        └── StringUtils.java
1 为包装器文件生成的文件夹
2 生成的版本目录
3 Gradle 包装器启动脚本
4 定义构建名称和子项目的设置文件
5 buildSrc 的构建脚本,用于配置构建逻辑的依赖项
6 用 Groovy 或 Kotlin DSL 编写的约定插件的源文件夹
7 三个子项目(applistutilities)的构建脚本
8 每个子项目中的 Groovy 源文件夹
9 子项目中的 Groovy 测试源文件夹

您现在已经设置好项目,可以构建一个模块化为多个子项目的 Groovy 应用程序。

审查项目文件

settings.gradle(.kts) 文件有两行值得注意

settings.gradle.kts
rootProject.name = "demo"
include("app", "list", "utilities")
settings.gradle
rootProject.name = 'demo'
include('app', 'list', 'utilities')
  • rootProject.name 为构建分配一个名称,这会覆盖根据其所在目录命名构建的默认行为。建议设置一个固定名称,因为如果项目共享(例如作为 Git 仓库的根),文件夹可能会更改。

  • include("app", "list", "utilities") 定义构建由相应文件夹中的三个子项目组成。可以通过扩展列表或添加更多 include(…​) 语句来添加更多子项目。

由于我们的构建由多个子项目组成,我们希望在它们之间共享构建逻辑和配置。为此,我们利用了位于 buildSrc 文件夹中的所谓约定插件buildSrc 中的约定插件是利用 Gradle 的插件系统编写可重用构建配置的简便方法。

在此示例中,我们可以找到三个基于彼此的约定插件

buildSrc/src/main/kotlin/buildlogic.groovy-common-conventions.gradle.kts
plugins {
    groovy (1)
}

repositories {
    mavenCentral() (2)
}

dependencies {
    constraints {
        implementation("org.apache.commons:commons-text:1.13.0") (3)

        implementation("org.apache.groovy:groovy-all:4.0.27")
    }

    implementation("org.apache.groovy:groovy-all") (4)

    testImplementation("org.junit.jupiter:junit-jupiter:5.12.1") (5)

    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

tasks.named<Test>("test") {
    useJUnitPlatform() (6)
}
buildSrc/src/main/groovy/buildlogic.groovy-common-conventions.gradle
plugins {
    id 'groovy' (1)
}

repositories {
    mavenCentral() (2)
}

dependencies {
    constraints {
        implementation 'org.apache.commons:commons-text:1.13.0' (3)

        implementation 'org.apache.groovy:groovy-all:4.0.27'
    }

    implementation 'org.apache.groovy:groovy-all' (4)

    testImplementation 'org.junit.jupiter:junit-jupiter:5.12.1' (5)

    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.named('test') {
    useJUnitPlatform() (6)
}

groovy-common-conventions 定义了一些应该由所有 Groovy 项目共享的配置——无论它们是库还是实际应用程序。首先,我们应用 Groovy 插件 (1) 以便所有用于构建 Groovy 项目的功能都可用。然后,我们声明一个仓库——mavenCentral()——作为外部依赖项的来源 (2),定义依赖项约束 (3) 以及所有子项目共享的标准依赖项,并将 JUnit 5 设置为测试框架 (4…​)。其他共享设置,如编译器标志或 JVM 版本兼容性,也可以在此处设置。

buildSrc/src/main/kotlin/buildlogic.groovy-library-conventions.gradle.kts
plugins {
    id("buildlogic.groovy-common-conventions") (1)
    `java-library` (2)
}
buildSrc/src/main/groovy/buildlogic.groovy-library-conventions.gradle
plugins {
    id 'buildlogic.groovy-common-conventions' (1)
    id 'java-library' (2)
}
buildSrc/src/main/kotlin/buildlogic.groovy-application-conventions.gradle.kts
plugins {
    id("buildlogic.groovy-common-conventions") (1)
    application (2)
}
buildSrc/src/main/groovy/buildlogic.groovy-application-conventions.gradle
plugins {
    id 'buildlogic.groovy-common-conventions' (1)
    id 'application' (2)
}

groovy-library-conventionsgroovy-application-conventions 都应用了 groovy-common-conventions 插件 (1),这样那里执行的配置就可以被库和应用程序项目共享。接下来,它们分别应用 java-libraryapplication 插件 (2),从而将我们的通用配置逻辑与库或应用程序的特定配置相结合。虽然此示例中没有更细粒度的配置,但库或应用程序项目特定的构建配置可以放入这些约定插件脚本之一中。

让我们看看子项目中的 build.gradle(.kts) 文件。

app/build.gradle.kts
plugins {
    id("buildlogic.groovy-application-conventions")
}

dependencies {
    implementation("org.apache.commons:commons-text")
    implementation(project(":utilities"))
}

application {
    mainClass = "demo.app.App" (1)
}
app/build.gradle
plugins {
    id 'buildlogic.groovy-application-conventions'
}

dependencies {
    implementation 'org.apache.commons:commons-text'
    implementation project(':utilities')
}

application {
    mainClass = 'demo.app.App' (1)
}
list/build.gradle.kts
plugins {
    id("buildlogic.groovy-library-conventions")
}
list/build.gradle
plugins {
    id 'buildlogic.groovy-library-conventions'
}
utilities/build.gradle.kts
plugins {
    id("buildlogic.groovy-library-conventions")
}

dependencies {
    api(project(":list"))
}
utilities/build.gradle
plugins {
    id 'buildlogic.groovy-library-conventions'
}

dependencies {
    api project(':list')
}

查看构建脚本,我们可以看到它们最多包含三个块

  • 每个构建脚本都应该有一个 `plugins {}` 块来应用插件。在一个结构良好的构建中,它可能只应用一个约定插件,如本例所示。约定插件将负责应用和配置核心 Gradle 插件(如 `application` 或 `java-library`)或其他来自插件门户的约定插件或社区插件。

  • 其次,如果项目有依赖项,则应添加一个 dependencies {} 块。依赖项可以是外部的,例如我们在 groovy-common-conventions 中添加的 JUnit 依赖项,也可以指向其他本地子项目。为此,使用 project(…​) 表示法。在我们的示例中,utilities 库需要 list 库。而 app 使用 utilities 库。如果本地项目相互依赖,Gradle 会在需要时(且仅在需要时)负责构建依赖项目。要了解更多信息,请参阅关于 Gradle 中的依赖管理 的文档。

  • 第三,可能有一个或多个插件配置块。这些配置块应仅在构建脚本中直接使用,如果它们仅配置一个特定项目的内容。否则,此类配置也应属于约定插件。在此示例中,我们使用 application {} 块,该块特定于 application 插件,用于将 app 项目中的 mainClass 设置为 demo.app.App (1)

我们拥有的最后一个构建文件是 buildSrc 中的 build.gradle(.kts) 文件。

buildSrc/build.gradle.kts
plugins {
    `kotlin-dsl` (1)
}

repositories {
    gradlePluginPortal() (2)
}
buildSrc/build.gradle
plugins {
    id 'groovy-gradle-plugin' (1)
}

repositories {
    gradlePluginPortal() (2)
}

此文件为构建约定插件本身奠定了基础。通过应用插件开发插件之一——groovy-gradle-pluginkotlin-dsl——(1),我们可以在 buildSrc 中以构建文件的形式编写约定插件。这些就是我们上面已经检查过的约定插件。此外,我们添加了 Gradle 的插件门户作为仓库 (2),这使我们能够访问社区插件。要使用插件,需要将其声明为 dependencies {} 块中的依赖项。

除了 Gradle 构建文件外,您还可以在相应的文件夹中找到 Groovy 源代码和测试源代码示例。您可以随意修改这些生成的源代码和测试,以探索 Gradle 在按以下描述运行构建时如何对更改做出反应。

运行测试

您可以使用 ./gradlew check 在所有子项目中执行所有测试。当您使用像 check 这样的纯任务名称调用 Gradle 时,该任务将为所有提供它的子项目执行。要只针对特定的子项目,您可以使用任务的完整路径。例如 :app:check 将只执行 app 项目的测试。但是,在此示例中,其他子项目仍将编译,因为 app 声明了对它们的依赖。

$ ./gradlew check

BUILD SUCCESSFUL
9 actionable tasks: 9 executed

如果所有测试都成功通过,Gradle 将不会在控制台打印更多输出。您可以在 <subproject>/build/reports 文件夹中找到测试报告。随意更改一些示例代码或测试,然后重新运行 check,看看如果测试失败会发生什么。

运行应用程序

多亏了 application 插件,您可以直接从命令行运行应用程序。run 任务告诉 Gradle 执行分配给 mainClass 属性的类中的 main 方法。

$ ./gradlew run

> Task :app:run
Hello world!

BUILD SUCCESSFUL
2 actionable tasks: 2 executed
首次运行 wrapper 脚本 gradlew 时,可能需要一些延迟,因为该版本的 gradle 会被下载并本地存储在您的 ~/.gradle/wrapper/dists 文件夹中。

捆绑应用程序

application 插件还会为您捆绑应用程序及其所有依赖项。存档还将包含一个脚本,用于通过一个命令启动应用程序。

$ ./gradlew build

BUILD SUCCESSFUL in 0s
7 actionable tasks: 7 executed

如果您运行如上所示的完整构建,Gradle 将为您生成两种格式的存档:app/build/distributions/app.tarapp/build/distributions/app.zip

发布构建扫描

了解您的构建在幕后做什么的最佳方法是发布 构建扫描。为此,只需使用 --scan 标志运行 Gradle 即可。

$ ./gradlew build --scan

BUILD SUCCESSFUL in 0s
7 actionable tasks: 7 executed

Publishing a Build Scan to scans.gradle.com requires accepting the Gradle Terms of Service defined at https://gradle.com/terms-of-service.
Do you accept these terms? [yes, no] yes

Gradle Terms of Service accepted.

Publishing Build Scan...
https://gradle.com/s/5u4w3gxeurtd2

点击链接,探索执行了哪些任务,下载了哪些依赖项以及更多详细信息!

总结

就这样!您现在已经成功地使用 Gradle 配置并构建了一个 Groovy 应用程序项目。您已经学会了如何

  • 初始化一个生成 Groovy 应用程序的项目

  • 通过组合多个子项目来创建模块化软件项目

  • 使用 buildSrc 中的约定插件在子项目之间共享构建配置逻辑

  • 在所有子项目中运行同名任务

  • 在特定子项目中运行任务

  • 构建、捆绑和运行应用程序

后续步骤

随着您的项目增长,您可能对如何配置 JVM 项目、构建多项目构建和依赖管理的更多详细信息感兴趣