您可以使用 IntelliJ 原生导入器Eclipse Buildship 在 IDE 中打开此示例。

为了让您的软件项目为增长做好准备,您可以将 Gradle 项目组织成多个子项目,以模块化您正在构建的软件。在本指南中,您将学习如何在 Java 应用程序的示例中构建这样的项目。但是,一般概念适用于您使用 Gradle 构建的任何软件。您可以按照本指南逐步创建一个新项目,也可以使用上面的链接下载完整的示例项目。

您将构建什么

您将构建一个 Java 应用程序,它包含一个应用程序和多个项目。

您需要什么

创建项目文件夹

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

第一步是为新项目创建一个文件夹,并更改到该文件夹。

$ mkdir demo
$ cd demo

运行 init 任务

在新项目目录中,使用终端中的以下命令运行 init 任务:gradle init。出现提示时,选择 1: application 项目类型和 1: Java 作为实现语言。之后,选择 2: Add library projects。接下来,您可以选择用于编写构建脚本的 DSL - 1 : Groovy2: Kotlin。对于其他问题,按回车键使用默认值。

输出将如下所示

$ gradle init

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 1

Split functionality across multiple subprojects?:
   1: no - only one application project
   2: yes - application and library projects
Enter selection (default: no - only one application project) [1..2] 2

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

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

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

Project name (default: demo):
Source package (default: demo):


BUILD SUCCESSFUL
2 actionable tasks: 2 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 为包装器文件生成的文件夹
2 生成的版本目录
3 Gradle 包装器启动脚本
4 设置文件,用于定义构建名称和子项目
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.11.0") (3)
    }

    testImplementation("org.junit.jupiter:junit-jupiter:5.10.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.11.0' (3)
    }

    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.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
第一次运行包装器脚本 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 配置和构建 Java 应用程序项目。您已了解如何

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

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

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

  • 在所有子项目中运行类似名称的任务

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

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

下一步

当您的项目发展壮大时,您可能对如何配置 JVM 项目、构建多项目结构以及依赖项管理有更多了解