Gradle 最佳实践
my-project/ ├── build.gradle.kts (根项目配置) ├── settings.gradle.kts ├── app/ (主业务模块) │ └── build.gradle.kts ├── core/ (核心工具、领域模型) │ └── build.gradle.kts └── infrastructure/ (数据库、外部服务适配器) └── build.gradle.kts
**关键设置**:
- **`settings.gradle.kts`** 中声明子模块:
```kotlin
rootProject.name = "my-project"
include("app", "core", "infrastructure")
- 子模块间通过
implementation(project(":core"))声明依赖。 - 大项目推荐按功能或层级划分模块,避免深层嵌套,一般不超过 3-4 层。
3. 依赖管理:集中版本与严格冲突解决
混乱的版本声明是运行时错误的温床。集中管理可以提升可维护性。
最佳实践:
-
使用版本目录(Version Catalog):在
gradle/libs.versions.toml中统一定义所有库版本。[versions] spring-boot = "3.3.4" kotlin = "2.0.20" [libraries] spring-boot-starter-web = { module = "org.springframework.boot:spring-boot-starter-web", version.ref = "spring-boot" } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } [plugins] spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot" }然后在
build.gradle.kts中引用:dependencies { implementation(libs.spring.boot.starter.web) } -
启用依赖锁定:生成
gradle.lockfile以确保二级依赖的版本不变。dependencyLocking { lockAllConfigurations() }使用
./gradlew dependencies --write-locks生成或更新锁定文件。 -
使用严格版本冲突解决策略:
configurations.all { resolutionStrategy { // 遇到版本冲突直接失败,迫使开发者显式解决 failOnVersionConflict() // 或者强制特定版本 force("com.squareup.okhttp3:okhttp:4.12.0") } }
4. 优化构建速度:配置缓存与并行构建
Gradle 提供了强大的增量构建能力,但需要正确开启。
必须开启的特性:
-
配置缓存(Configuration Cache):缓存 “构建配置” 阶段的结果,显著缩短流水线。在项目根目录
gradle.properties中添加:org.gradle.configuration-cache=true org.gradle.unsafe.configuration-cache.max-problems=0 -
构建缓存(Build Cache):共享和复用任务输出。本地开启:
org.gradle.caching=true在 CI 环境推荐设置 远程缓存节点(如 Gradle Enterprise 或兼容的 HTTP 后端)。
-
并行执行:开启多模块并行构建:
org.gradle.parallel=true -
增加 JVM 堆内存:
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -
避免动态任务创建与无意义配置:尽量使用懒创建(如
tasks.register())而非tasks.create()。
5. 使用 Kotlin DSL 编写构建脚本
为何转向 Kotlin DSL:
- 静态类型,IDE 自动补全、跳转、重构支持极佳。
- 脚本和项目代码使用同一种语言,降低上下文切换成本。
- 编译期错误检查,避免 Groovy 脚本运行时的意外错误。
迁移小贴士:
- 将
build.gradle重命名为build.gradle.kts。 - 函数调用需要使用括号,赋值用
=替代 Groovy 的=混淆。 - 示例对比:
// Groovy task copyFiles(type: Copy) { from 'src' into 'dest' } // Kotlin DSL tasks.register<Copy>("copyFiles") { from("src") into("dest") }
6. 任务编写原则:幂等、增量与缓存
良好的任务设计让构建可预测且快速。
- 显式声明输入与输出:使用
@Input,@OutputDirectory,@InputFiles等注解,以便 Gradle 启用增量构建。abstract class GenerateReport : DefaultTask() { @get:InputFiles abstract val dataFiles: ConfigurableFileCollection @get:OutputFile abstract val reportFile: RegularFileProperty @TaskAction fun execute() { // 仅当输入更改时才运行 } } - 避免在任务中执行副作用:所有副作用应限于
@Output目录或文件,不要修改项目源目录。 - 清理构建:自定义任务应正确贡献到
clean任务,通常通过将输出放在build目录下自动完成。
7. 依赖配置最佳选择:api vs implementation
在多模块或库项目中,暴露不必要的传递依赖会造成耦合和编译慢。
implementation:依赖只在模块内部可用,编译路径不会传染给下游模块。首选这一配置。api:依赖会传递给所有依赖该模块的项目。仅在公共 API 返回类型或参数中使用时才声明为api。compileOnly和runtimeOnly分别用于编译时可见但运行时由容器提供,或编译时不需要但运行时必需的库。
示例:
dependencies {
// 内部实现细节,不泄露
implementation("com.google.guava:guava:33.3.0-jre")
// 公开 API 中返回了某类型,所以必须 api
api("org.apache.commons:commons-lang3:3.14.0")
compileOnly("org.projectlombok:lombok:1.18.34") // 注解处理器由编译器处理
annotationProcessor("org.projectlombok:lombok:1.18.34")
}
8. 编写可复用的插件与约定(Convention Plugins)
在多模块项目中,许多配置是重复的(如编译器设置、测试框架)。使用约定插件避免重复代码。
步骤:
- 在
buildSrc/src/main/kotlin/my-java-convention.gradle.kts中定义通用配置:plugins { java } java { toolchain { languageVersion = JavaLanguageVersion.of(21) } } testing { suites.named("test") { useJUnitJupiter() } } - 在子模块中应用:
plugins { id("my-java-convention") } - 更成熟的方案是使用包含插件(Included Builds)或独立发布插件,但
buildSrc对于中小型项目已足够。
9. 健康检查:构建扫描与依赖报告
构建扫描(Build Scan):无需本地安装,运行 ./gradlew build --scan,在输出链接中查看性能分析、任务依赖图、配置时间等。诊断速度问题时首先做一次扫描。
依赖分析:
./gradlew :app:dependencies --configuration runtimeClasspath查看传递依赖树。- 使用插件
com.autonomousapps.dependency-analysis自动发现未使用的依赖和错误配置。
运行plugins { id("com.autonomousapps.dependency-analysis") version "1.32.0" }./gradlew buildHealth可获得详细报告。
版本兼容性检查:使用 gradle-versions-plugin 检测可用的依赖更新和 Gradle 版本升级。
plugins {
id("com.github.ben-manes.versions") version "0.51.0"
}
// 任务: ./gradlew dependencyUpdates -Drevision=release
10. 持续集成优化
- 仅构建和测试变更的模块:利用 Gradle 的依赖图,结合
--changed参数(需要 git):./gradlew build --changed