随着项目的发展,通常会将其拆分为更小、更集中的模块,这些模块一起构建、测试和发布。Gradle 通过多项目构建支持这一点,允许您在单个构建下组织相关的代码库,同时保持每个模块的逻辑隔离。
多项目布局
一个多项目构建由一个根项目和一个或多个子项目组成,所有这些都在单个 settings.gradle(.kts)
文件中定义。这种结构支持模块化、并行执行和代码重用。

典型的多项目结构如下所示
my-project/
├── settings.gradle.kts (1)
├── build.gradle.kts (2)
├── app/ (3)
│ └── build.gradle.kts (4)
├── core/ (3)
│ └── build.gradle.kts (5)
└── util/ (3)
└── build.gradle.kts (6)
my-project/
├── settings.gradle (1)
├── build.gradle (2)
├── app/ (3)
│ └── build.gradle (4)
├── core/ (3)
│ └── build.gradle (5)
└── util/ (3)
└── build.gradle (6)
1 | 声明子项目 |
2 | 根项目构建逻辑(可选) |
3 | 子项目 |
4 | 应用模块 |
5 | 共享核心逻辑 |
6 | 工具代码 |
每个子项目都可以定义自己的构建逻辑、依赖和插件。
在 settings.gradle(.kts)
中,您可以使用 include()
按名称包含子项目。include()
方法接受 项目路径 作为参数
rootProject.name = "my-project"
include("app", "core", "util")
rootProject.name = 'my-project'
include('app', 'core', 'util')
默认情况下,项目路径对应于项目目录的相对物理位置。例如,路径 services:api
映射到相对于根项目的目录 ./services/api
。
您可以在 Settings.include(String…)
的 DSL 参考中找到更多示例和详细用法。
项目描述符
为了进一步向 Gradle 描述项目架构,设置文件提供了项目描述符。
您可以随时在设置文件中修改这些描述符。
要访问描述符,您可以
include("project-a")
println(rootProject.name)
println(project(":project-a").name)
include('project-a')
println rootProject.name
println project(':project-a').name
使用此描述符,您可以更改项目的名称、项目目录和构建文件
rootProject.name = "main"
include("project-a")
project(":project-a").projectDir = file("custom/my-project-a")
project(":project-a").buildFileName = "project-a.gradle.kts"
rootProject.name = 'main'
include('project-a')
project(':project-a').projectDir = file('custom/my-project-a')
project(':project-a').buildFileName = 'project-a.gradle'
有关更多信息,请查阅 API 文档中的 ProjectDescriptor 类。
仅包含现有项目
当您在 settings.gradle(.kts)
文件中包含一个子项目时,Gradle 会期望该项目关联的目录存在且可写。
从 Gradle 9.0 开始,这将严格执行:如果项目目录缺失或只读,构建将失败。 这取代了 Gradle 以前静默允许缺失项目目录的行为。 |
以下是您如何在配置时创建缺失目录的方法
include("project-without-directory")
project(":project-without-directory").projectDir.mkdirs()
include 'project-without-directory'
project(":project-without-directory").projectDir.mkdirs()
修改子项目路径
让我们来看一个具有以下结构的假设项目
.
├── settings.gradle.kts
├── app/
│ ├── build.gradle.kts
│ └── src/
└── subs/ (1)
├── build.gradle.kts
└── web (1)
└── my-web-module (2)
.
├── settings.gradle
├── app/
│ ├── build.gradle
│ └── src/
└── subs/ (1)
├── build.gradle
└── web (1)
└── my-web-module (2)
1 | Gradle 可能会将其视为一个子项目 |
2 | 实际预期的子项目 |
如果您的 settings.gradle(.kts)
看起来像这样
include(':subs:web:my-web-module')
Gradle 会看到一个逻辑项目名为 :subs:web:my-web-module
的子项目,以及另外两个可能无意的逻辑项目名称为
-
:subs
-
:subs:web
这可能导致虚拟构建目录,尤其是在使用 allprojects{}
或 subproject{}
时。
为避免这种情况,您可以使用
include(':my-web-module')
project(':my-web-module').projectDir = "subs/web/my-web-module"
这样您最终只会得到一个名为 :my-web-module
的子项目。
因此,尽管物理项目布局相同,但逻辑结果却不同。
命名建议
随着项目的发展,命名和一致性变得越来越重要。
为了保持构建的可维护性,我们建议以下几点
-
保留子项目的默认项目名称:可以在设置文件中配置自定义项目名称。但是,这会给开发人员带来不必要的额外工作,以跟踪哪些项目属于哪些文件夹。
-
所有项目名称都使用小写连字符:所有字母都小写,单词之间用破折号 (
-
) 分隔。 -
在设置文件中定义根项目名称:
rootProject.name
有效地为构建分配了一个名称,用于构建扫描等报告中。如果未设置根项目名称,则名称将是容器目录名称,这可能不稳定(即,您可以在任何目录中检出项目)。如果未设置根项目名称并将其检出到文件系统的根目录(例如/
或C:\
),则名称将随机生成。
声明子项目之间的依赖
如果一个子项目依赖于另一个子项目怎么办?如果一个子项目依赖于另一个子项目产生的工件怎么办?

这是多项目构建中的常见用例。Gradle 通过 项目依赖 支持此场景。
依赖于另一个项目
考虑一个具有以下布局的多项目构建
.
├── api
│ ├── src
│ │ └──...
│ └── build.gradle.kts
├── services
│ └── person-service
│ ├── src
│ │ └──...
│ └── build.gradle.kts
├── shared
│ ├── src
│ │ └──...
│ └── build.gradle.kts
└── settings.gradle.kts
.
├── api
│ ├── src
│ │ └──...
│ └── build.gradle
├── services
│ └── person-service
│ ├── src
│ │ └──...
│ └── build.gradle
├── shared
│ ├── src
│ │ └──...
│ └── build.gradle
└── settings.gradle
在此示例中
-
person-service
依赖于api
和shared
-
api
依赖于shared
您使用项目路径声明这些关系,项目路径使用冒号 (:
) 表示嵌套。例如
-
:shared
指的是shared
子项目 -
services:person-service
指的是一个嵌套子项目
rootProject.name = "dependencies-java"
include("api", "shared", "services:person-service")
plugins {
id("java")
}
repositories {
mavenCentral()
}
dependencies {
testImplementation("junit:junit:4.13")
}
plugins {
id("java")
}
repositories {
mavenCentral()
}
dependencies {
testImplementation("junit:junit:4.13")
implementation(project(":shared"))
}
plugins {
id("java")
}
repositories {
mavenCentral()
}
dependencies {
testImplementation("junit:junit:4.13")
implementation(project(":shared"))
implementation(project(":api"))
}
rootProject.name = 'basic-dependencies'
include 'api', 'shared', 'services:person-service'
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
testImplementation "junit:junit:4.13"
}
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
testImplementation "junit:junit:4.13"
implementation project(':shared')
}
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
testImplementation "junit:junit:4.13"
implementation project(':shared')
implementation project(':api')
}
有关项目路径的更多详细信息,请查阅 Settings.include(String…) 的 DSL 文档。
项目依赖会影响构建顺序和类路径
-
所需的项目将首先构建。
-
其编译后的类和传递依赖将添加到消费项目的类路径中。
例如,运行 ./gradlew :api:compileJava
将首先构建 shared
,然后构建 api
。
依赖于另一个项目产生的工件
有时,您只需要另一个项目中特定任务的输出——而不是整个项目本身。
虽然您可以在项目之间创建任务到任务的依赖,但 Gradle 不鼓励这样做,因为它会在任务之间创建紧密耦合。
相反,使用输出工件来暴露任务的输出并将其建模为依赖。Gradle 的变体感知依赖管理允许一个项目以结构化、按需的方式消费另一个项目的工件。