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

为了让您的软件项目做好成长的准备,您可以将一个 Gradle 项目组织成多个子项目,以模块化您正在构建的软件。在本指南中,您将学习如何以 Java 应用程序为例构建此类项目。然而,这些通用概念适用于您使用 Gradle 构建的任何软件。您可以按照本指南的步骤从头开始创建新项目,或者使用上面的链接下载完整的示例项目。

您将构建什么

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

您需要什么

创建项目文件夹

Gradle 提供一个内置任务 init,可以在空文件夹中初始化新的 Gradle 项目。init 任务会使用(同样内置的)wrapper 任务来创建 Gradle wrapper 脚本 gradlew

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

$ mkdir demo
$ cd demo

运行 init 任务

在新项目目录内,在终端中运行以下命令来执行 init 任务:gradle init。当出现提示时,选择项目类型 1: application 和实现语言 1: Java。然后,选择 2: Application and library project。接下来,您可以选择用于编写构建脚本的 DSL - 1 : Kotlin2: 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]  1

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]

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection (default: JUnit Jupiter) [1..4]

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.java-application-conventions.gradle.kts
│               ├── buildlogic.java-common-conventions.gradle.kts
│               └── buildlogic.java-library-conventions.gradle.kts
├── app
│   ├── build.gradle.kts (7)
│   └── src
│       ├── main (8)
│       │   └── java
│       │       └── demo
│       │           └── app
│       │               ├── App.java
│       │               └── MessageUtils.java
│       └── test (9)
│           └── java
│               └── demo
│                   └── app
│                       └── MessageUtilsTest.java
├── list
│   ├── build.gradle.kts (7)
│   └── src
│       ├── main (8)
│       │   └── java
│       │       └── demo
│       │           └── list
│       │               └── LinkedList.java
│       └── test (9)
│           └── java
│               └── demo
│                   └── list
│                       └── LinkedListTest.java
└── utilities
    ├── build.gradle.kts (7)
    └── src
        └── main (8)
            └── java
                └── demo
                    └── utilities
                        ├── JoinUtils.java
                        ├── SplitUtils.java
                        └── StringUtils.java
├── 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.java-application-conventions.gradle
│               ├── buildlogic.java-common-conventions.gradle
│               └── buildlogic.java-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 为 wrapper 文件生成的文件夹
2 生成版本目录
3 Gradle wrapper 启动脚本
4 定义构建名称和子项目的 Settings 文件
5 用于配置构建逻辑依赖的 buildSrc 构建脚本
6 用于编写 Groovy 或 Kotlin DSL 约定插件的源代码文件夹
7 三个子项目(applistutilities)的构建脚本
8 每个子项目中的 Java 源代码文件夹
9 子项目中的 Java 测试源代码文件夹

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

查看项目文件

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.java-common-conventions.gradle.kts
plugins {
    java (1)
}

repositories {
    mavenCentral() (2)
}

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

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

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

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

repositories {
    mavenCentral() (2)
}

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

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

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

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

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

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

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

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

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

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

application {
    mainClass = "demo.app.App" (1)
}
app/build.gradle
plugins {
    id 'buildlogic.java-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.java-library-conventions")
}
list/build.gradle
plugins {
    id 'buildlogic.java-library-conventions'
}
utilities/build.gradle.kts
plugins {
    id("buildlogic.java-library-conventions")
}

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

dependencies {
    api project(':list')
}

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

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

  • 其次,如果项目有依赖项,应该添加一个 dependencies {} 块。依赖项可以是外部的,比如我们在 java-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 构建文件外,您还可以在相应的文件夹中找到示例 Java 源代码和测试源代码。您可以随意修改这些生成的源文件和测试,以探索 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

发布构建扫描

了解构建幕后工作原理的最佳方法是发布构建扫描。为此,只需运行 Gradle 并带上 --scan 标志。

$ ./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 构建了一个 Java 应用程序项目。您学习了如何:

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

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

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

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

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

  • 构建、打包并运行应用程序

下一步

当您的项目发展壮大时,您可能对如何配置 JVM 项目、组织多项目构建和依赖管理等更多细节感兴趣