Gradle 版本演进全景:构建工具的进化密码

📅 2026/7/27 16:06:01
Gradle 版本演进全景:构建工具的进化密码
开篇为什么你需要读懂 Gradle 的版本演进如果你是一位 JVM 生态的开发者几乎不可能绕开 Gradle。从 Android Studio 默认的构建工具到 Spring Boot 项目的脚手架再到大数据组件的源码编译Gradle 已经成为现代构建领域的事实标准之一。然而很多开发者对 Gradle 的认知停留在能跑就行的阶段遇到构建慢、依赖冲突、缓存失效等问题时只能反复重启 Daemon 或清空 .gradle 目录。Gradle 从 5.0 到 9.0 经历了五次大版本跃迁每一次都伴随着构建模型、依赖体系、性能范式的深刻变化。读懂这些变化意味着你能用更少的代码描述更复杂的依赖关系用更短的等待时间完成更大规模的构建用更安全的方式引入第三方库。本文将从最基础的构建脚本讲起按版本顺序梳理每个里程碑带来的用户视角变化并提供可直接复制的代码用例。无论你是刚接触 Gradle 的新手还是从 Maven 迁移过来的老兵都能从中找到属于自己的升级路径。Gradle 的核心心智模型构建脚本的三阶段生命周期Gradle 的每一次构建都经历三个阶段初始化、配置、执行。初始化阶段确定参与构建的项目集合Gradle 会执行 settings.gradle(.kts) 文件决定哪些子项目被纳入构建。配置阶段会执行所有参与项目的 build.gradle(.kts) 文件构建出完整的任务依赖图但此时任务本身还没有真正执行。执行阶段则按照依赖顺序依次运行被请求的任务。理解这三个阶段对调试构建问题至关重要。如果你在配置阶段执行了耗时操作比如读取文件、发起网络请求那么即使只是运行 gradle help 也会变慢。配置缓存Configuration Cache正是为了消除配置阶段的重复开销而生的特性后文会详细展开。下面这段脚本展示了三阶段的可观察行为// settings.gradlerootProject.namedemoincludeapp,core// build.gradleprintln配置阶段项目名 $project.nametasks.register(hello){println配置阶段注册 hello 任务doLast{println执行阶段hello 任务运行}}运行gradle hello时你会先看到两行配置阶段的输出再看到执行阶段的输出。如果再运行一次配置阶段的输出依然会出现因为传统模式下每次构建都会重新执行配置阶段。任务、依赖与项目任务是 Gradle 构建的最小执行单元每个任务有输入、输出和动作。依赖则是任务之间的拓扑关系Gradle 会根据依赖关系决定执行顺序。项目是组织代码的容器一个 Gradle 构建可以包含多个项目每个项目可以有自己的构建脚本和依赖声明。任务之间通过dependsOn建立依赖Gradle 会自动按拓扑顺序执行。任务的输入输出用于增量构建和缓存复用如果输入没变任务会被跳过。下面是一个典型的任务依赖示例tasks.register(compile){doLast{println编译源代码}}tasks.register(test){dependsOncompiledoLast{println运行测试}}tasks.register(build){dependsOntestdoLast{println打包发布}}运行gradle build时Gradle 会先执行 compile再执行 test最后执行 build。这种声明式的依赖描述是 Gradle 区别于 Make 等工具的核心特征之一。插件与约定插件是 Gradle 复用构建逻辑的主要机制。一个插件可以添加任务、配置依赖、扩展 DSL、注册扩展点。Gradle 内置了大量核心插件如 java、application、maven-publish 等社区还有成千上万的第三方插件。插件通过plugins {}块应用plugins{idjavaidapplication}application{mainClasscom.example.Main}应用 java 插件后Gradle 会自动添加 compileJava、test、jar、build 等任务并约定 src/main/java、src/test/java 等目录结构。这种约定优于配置的思想借鉴自 Maven但 Gradle 允许你通过 sourceSets 等扩展点自由覆盖约定。基础功能使用创建第一个 Gradle 项目Gradle 提供了init任务用于生成项目骨架。在空目录执行gradle initGradle 会交互式询问项目类型application、library 等、构建脚本语言Groovy 或 Kotlin DSL、测试框架等然后生成完整的脚手架。生成的项目包含 settings 文件、build 文件、Wrapper 脚本和示例源码。Wrapper 是 Gradle 的精髓之一。它把 Gradle 发行版的下载和版本管理交给项目自身确保团队成员和 CI 服务器使用完全相同的 Gradle 版本。生成 Wrapper 的命令是gradle wrapper --gradle-version8.5生成的 gradlew、gradlew.bat 和 gradle-wrapper.jar 应该提交到版本控制。下面是一个典型的项目结构my-app/ ├── settings.gradle ├── build.gradle ├── gradlew ├── gradlew.bat ├── gradle/wrapper/ │ └── gradle-wrapper.properties └── src/ ├── main/java/ └── test/java/声明依赖依赖声明是构建脚本最常见的操作。Gradle 通过 configurations 把依赖分组到不同的用途编译、运行、测试等java 插件预定义了 implementation、api、testImplementation、runtimeOnly 等配置。下面是一个典型的依赖声明plugins{idjava}repositories{mavenCentral()}dependencies{implementationcom.google.guava:guava:32.1.3-jreimplementationorg.slf4j:slf4j-api:2.0.9runtimeOnlych.qos.logback:logback-classic:1.4.11testImplementationorg.junit.jupiter:junit-jupiter:5.10.0testRuntimeOnlyorg.junit.platform:junit-platform-launcher}implementation 和 api 的区别是 Gradle 6.0 之后才被广泛强调的implementation 隐藏依赖的传递性api 则暴露给下游消费者。合理使用 implementation 可以显著减少重新编译的范围。配置仓库仓库是依赖的来源。Gradle 支持本地目录、Maven 仓库、Ivy 仓库等多种类型。最常见的是 mavenCentral()企业内部通常还有私有的 Nexus 或 Artifactory。从 Gradle 7.0 开始推荐在 settings.gradle 中集中声明仓库避免子项目各自为政// settings.gradledependencyResolutionManagement{repositories{mavenCentral()maven{urlhttps://repo.example.com/mavencredentials{usernamefindProperty(repoUser)passwordfindProperty(repoPass)}}}}这种集中式声明让仓库配置成为构建的单一事实来源子项目无法再添加额外仓库从而保证依赖来源的一致性和可审计性。自定义任务除了插件提供的任务开发者经常需要编写自定义任务。最简单的方式是用tasks.register注册一个闭包任务更复杂的需求则可以定义 Task 类。下面是一个复制文件的自定义任务示例tasks.register(copyDocs,Copy){fromsrc/main/asciidocintobuild/docs/htmlrename(.)\\.adoc,$1.html}tasks.register(release){dependsOnbuild,copyDocsdoLast{println发布版本${project.version}}}Copy 是 Gradle 内置的任务类型它已经实现了增量构建和缓存支持。继承内置任务类型比自己从零实现要省心得多这也是 Gradle 推荐的实践。Gradle 5Kotlin DSL 的正式登场生产可用的 Kotlin DSLGradle 5.0 于 2018 年发布最重要的里程碑是 Kotlin DSL 正式进入生产可用状态。在此之前Kotlin DSL 还处于孵化阶段API 不稳定性能也不理想。5.0 之后开发者可以在 .gradle.kts 文件中享受 IDE 的自动补全、类型检查、跳转定义等现代编辑器特性这对大型项目和团队协作意义重大。Kotlin DSL 的核心价值在于类型安全。Groovy DSL 灵活但容易写错配置名编译期不会报错只有运行时才发现。Kotlin DSL 则把大部分配置暴露为强类型 API错误在编辑器里就能看到。下面是同一个依赖声明在两种 DSL 下的对比// build.gradle.kts (Kotlin DSL)plugins{java}dependencies{implementation(com.google.guava:guava:32.1.3-jre)testImplementation(org.junit.jupiter:junit-jupiter:5.10.0)}// build.gradle (Groovy DSL)plugins{idjava}dependencies{implementationcom.google.guava:guava:32.1.3-jretestImplementationorg.junit.jupiter:junit-jupiter:5.10.0}Kotlin DSL 的缺点是编译速度较慢这个问题直到 Gradle 8.0 才得到显著改善。在 5.0 时代大型项目的 Kotlin DSL 配置阶段可能比 Groovy DSL 慢一倍以上。依赖版本对齐Gradle 5.0 引入了依赖版本对齐机制允许把一组相关依赖绑定到同一版本。这在 Jackson、Spring 等多模块库的场景下非常有用避免出现 jackson-databind 是 2.15 而 jackson-core 是 2.14 的不一致情况。对齐通过 platform 或 BOM 导入实现dependencies{implementationplatform(com.fasterxml.jackson:jackson-bom:2.15.2)implementationcom.fasterxml.jackson.core:jackson-databindimplementationcom.fasterxml.jackson.core:jackson-coreimplementationcom.fasterxml.jackson.core:jackson-annotations}导入 BOM 后所有 jackson-* 依赖都会自动使用 2.15.2 版本无需逐个声明。这个特性在 6.0 之后进一步完善与 Gradle 自有的 platform 概念融合。任务超时5.0 还引入了任务超时配置。某些任务如网络下载、外部命令调用可能因为环境问题卡死导致整个构建挂起。设置超时后任务超时会自动失败并释放资源tasks.register(downloadData){timeoutDuration.ofMinutes(10)doLast{// 下载大文件}}超时默认是关闭的需要显式设置。这个特性对 CI 流水线特别有用避免一个卡死的任务阻塞整个流水线。Gradle 6依赖管理的全面革新Gradle Module Metadata 默认启用Gradle 6.0 于 2019 年 11 月发布依赖管理是这次升级的重头戏。其中最底层也最重要的变化是 Gradle Module MetadataGMM默认启用。GMM 是 Gradle 自有的元数据格式扩展了 Maven POM 的能力支持变体、依赖约束、富版本、能力等高级特性。GMM 文件名为.module与 POM 并存。当使用 maven-publish 插件发布时Gradle 会同时生成 POM 和 .module 文件。下游消费者如果是 Gradle 6会优先读取 .module 文件从而获得变体感知等高级能力如果是 Maven 或老版本 Gradle则回退到 POM。这种向后兼容的设计让 GMM 可以平滑推广。对普通用户来说GMM 默认启用意味着你发布的库可以携带更丰富的元数据。例如你可以声明某个依赖只在编译时需要、某个依赖只在运行时需要消费者会自动选择正确的变体。下面是一个发布配置示例publishing{publications{mavenJava(MavenPublication){from components.java pom{nameMy LibrarydescriptionA demo library}}}}富版本约束富版本约束是 6.0 引入的最常用特性之一。传统的版本声明只能写一个固定版本号或区间富版本则允许表达偏好、“严格”、拒绝等多种语义。这在管理传递依赖时特别有用可以避免下游意外升级到不兼容的版本。dependencies{implementation(com.google.guava:guava){version{strictly[32.0,33.0[prefer32.1.3-jrereject32.0.0-jre}}}strictly 表示版本必须落在这个区间内否则构建失败prefer 表示在多个候选版本中优先选择reject 表示明确排除某些版本。这些约束会被写入 GMM对下游消费者生效。平台与 BOM 集成平台是 Gradle 自有的 BOM 概念用于集中声明一组依赖的推荐版本。与 Maven BOM 不同Gradle 平台可以包含富版本约束、能力声明等更丰富的信息。平台本身也是一个项目通过 java-platform 插件发布// platform 项目的 build.gradleplugins{idjava-platform}dependencies{constraints{apicom.google.guava:guava:32.1.3-jreapiorg.slf4j:slf4j-api:2.0.9apiorg.junit.jupiter:junit-jupiter:5.10.0}}消费端通过platform(project(:my-platform))或platform(com.example:my-platform:1.0)引入。平台与 BOM 可以互操作Gradle 既能消费 Maven BOM也能把自己的平台发布为 BOM 供 Maven 使用。依赖约束依赖约束用于影响传递依赖的版本而不是直接声明依赖。这是解决传递依赖版本过低问题的正确方式而不是直接把传递依赖提升为直接依赖。约束不会被发布为依赖只会作为版本建议dependencies{constraints{implementationcommons-codec:commons-codec:1.16.0}}如果某个传递依赖引入了 commons-codec约束会强制使用 1.16.0 版本。如果没有传递依赖引入它约束不会自动添加这个依赖。这种细粒度的控制是 6.0 之前难以做到的。组件能力组件能力用于检测和解决互斥依赖。最经典的例子是日志框架slf4j 有多个实现logback、log4j、slf4j-simple它们互斥只能选一个。6.0 之前Gradle 会在 classpath 上同时出现多个实现导致运行时警告。6.0 之后插件可以声明能力Gradle 会自动检测冲突并要求用户选择dependencies{implementationorg.slf4j:slf4j-api:2.0.9implementationch.qos.logback:logback-classic:1.4.11// 如果同时引入 log4j-slf4j-implGradle 会报能力冲突}社区插件可以为自己的依赖声明能力让 Gradle 帮助检测冲突。这是构建可观测性和正确性的重要进步。特性变体与测试夹具特性变体允许一个库暴露多个可选功能每个功能有自己的依赖。最常见的应用是测试夹具一个库可以发布主产物和测试夹具产物下游项目的测试代码可以依赖测试夹具而主代码不会引入测试夹具的依赖。plugins{idjava-libraryidjava-test-fixtures}dependencies{testFixturesImplementationorg.junit.jupiter:junit-jupiter:5.10.0}应用 java-test-fixtures 插件后src/testFixtures/java 目录下的代码会被编译为测试夹具产物并发布。下游项目可以这样消费dependencies{implementationcom.example:my-lib:1.0testImplementation(testFixtures(com.example:my-lib:1.0))}这种模式比传统的把测试工具类复制到每个项目或发布单独的 -test.jar要优雅得多。更快的增量编译6.0 对增量 Java 编译做了改进能够识别实现细节类避免不必要的重编译。例如类 A 被类 B 使用类 B 被类 C 使用当 A 改变时传统增量编译会重编译 A、B、C 三个类。6.0 之后如果 B 只是把 A 当作实现细节比如只在方法内部使用 A而不在签名中暴露 A则 C 不需要重编译。对于深层依赖链的大型项目这个优化可以显著减少增量编译的范围。底层实现基于对字节码的分析识别哪些类是 API 暴露的、哪些是内部实现。这个特性是自动启用的无需配置。Gradle 7性能与安全的双重跃迁文件系统监视默认启用Gradle 7.0 于 2021 年 5 月发布最直观的变化是增量构建更快了。这得益于文件系统监视File System Watching默认启用。在此之前Gradle 每次构建都要扫描所有输入文件计算快照对于大型项目这可能耗时数秒。启用文件系统监视后Gradle 会维护一个后台进程监听文件变化下次构建时直接查询变化记录跳过全量扫描。文件系统监视在 Linux、macOS、Windows 上都可用7.0 还增加了对 Apple SiliconM1的原生支持。用户无需任何配置就能享受这个加速这是 7.0 最受欢迎的改进之一。底层实现使用了操作系统原生的文件监听 API如 Linux 的 inotify、macOS 的 FSEvents。如果遇到文件系统监视的兼容性问题可以通过org.gradle.vfs.watchfalse关闭。但绝大多数情况下这个特性是稳定且有益的。配置缓存实验性配置缓存是 7.0 引入的最具前瞻性的特性。它缓存配置阶段的结果下次构建时如果构建脚本和输入没变直接复用缓存的任务图跳过整个配置阶段。对于配置阶段耗时较长的大型项目这可以节省数秒甚至数十秒。7.0 时代配置缓存还是实验性的需要显式启用# gradle.properties org.gradle.configuration-cachetrue启用后第一次构建会正常执行配置阶段并写入缓存第二次构建如果脚本没变则直接复用缓存。配置缓存对构建脚本和插件有严格要求不能在任务执行阶段访问 Project 对象、不能使用外部状态等。不兼容的插件会导致缓存失效或构建失败。配置缓存的底层原理是把任务图序列化到磁盘下次构建反序列化后直接执行任务。这要求任务闭包不捕获不可序列化的状态这也是为什么很多老插件需要改造才能兼容。这个特性在 8.0 和 9.0 逐步成熟最终在 9.0 成为首选执行模式。版本目录版本目录是 7.0 引入的依赖集中管理方案解决了多项目构建中依赖版本散落各处的问题。它用一个 TOML 文件集中声明所有依赖的坐标和版本子项目通过类型安全的方式引用# gradle/libs.versions.toml [versions] guava 32.1.3-jre junit 5.10.0 [libraries] guava { module com.google.guava:guava, version.ref guava } junit-jupiter { module org.junit.jupiter:junit-jupiter, version.ref junit } [plugins] java-library { id java-library, version none }// build.gradledependencies{implementation libs.guava testImplementation libs.junit.jupiter}Kotlin DSL 还能享受 IDE 的自动补全和类型检查。版本目录在 7.0 是实验性的7.4 之后稳定8.0 之后还支持在 plugins 块中使用别名。这是现代 Gradle 项目管理依赖的推荐方式。Java 工具链工具链让构建脚本声明项目需要的 JDK 版本Gradle 会自动从本地查找或从远程下载匹配的 JDK。这解决了开发机器 JDK 版本不一致的痛点也允许构建用 Java 17 编译同时用 Java 11 运行测试。java{toolchain{languageVersionJavaLanguageVersion.of(17)}}Gradle 会检测本地的 JDK 安装包括 SDKMAN!、jabba、asdf-vm 等包管理器安装的如果找不到匹配版本会从 AdoptOpenJDK 下载。工具链在 7.0 引入后续版本不断增强8.0 之后还支持指定 vendor、自动配置 Daemon JVM 等。依赖验证依赖验证是 7.0 的安全特性允许构建声明依赖的校验和和签名Gradle 会在解析依赖时验证。这可以防止依赖被篡改或供应链攻击。启用方式是生成验证元数据文件gradle --write-verification-metadata sha256help这会生成 gradle/verification-metadata.xml 文件记录所有依赖的 SHA-256 校验和。之后任何依赖的变更都会被检测未签名的依赖会被拒绝。对于安全敏感的项目如金融、医疗这是必备特性。单一依赖锁文件依赖锁定用于固定传递依赖的版本确保可重现构建。7.0 之前每个配置一个锁文件管理起来很麻烦。7.0 改为单一锁文件gradle/dependencies.lock所有配置的锁定状态集中管理。启用方式# gradle.properties dependencyLocking.lockAllConfigurations()gradle dependencies --write-locks锁文件应该提交到版本控制团队成员和 CI 都会使用相同的传递依赖版本。这与版本目录互补版本目录管理直接依赖锁文件管理传递依赖。Gradle 8开发者体验的精雕细琢Kotlin DSL 编译提速Gradle 8.0 于 2023 年 2 月发布Kotlin DSL 用户最直接的受益是编译速度提升约 20%。这得益于对 plugins {} 块的解释器优化8.0 之前plugins 块需要调用 Kotlin 编译器解析8.0 引入了一个轻量级解释器能直接处理标准格式的 plugins 块跳过编译器调用。要享受这个加速plugins 块必须使用受约束的语法plugins{javaid(com.example.plugin)version1.0kotlin(jvm)version1.9.0}如果使用了版本目录别名如alias(libs.plugins.example)或类型安全访问器解释器会回退到编译器模式无法享受加速。这种限制在后续版本逐步放宽。8.0 还把 Kotlin DSL 的 API 级别从 1.4 提升到 1.8开发者可以在构建脚本中使用 Kotlin 1.8 的所有语言特性如 sealed interface、context receiver 等。同时Kotlin DSL 脚本的编译目标从 Java 8 字节码改为运行 Gradle 的 JVM 版本如果团队用 Java 17 跑 Gradle构建脚本就能用 Java 17 的库。配置缓存的成熟8.0 时代配置缓存从实验性走向稳定最显著的改进是首次构建也能并行执行任务。在此之前配置缓存只在缓存命中时启用并行首次构建缓存未命中仍然串行执行配置阶段。8.0 之后即使缓存未命中任务也会在配置阶段完成后立即并行执行。# gradle.properties org.gradle.configuration-cachetrue org.gradle.paralleltrue8.1 引入了配置缓存加密用机器特定的密钥加密缓存文件防止敏感数据如仓库凭证泄露。8.10 引入了字符串去重大幅减小缓存文件体积AndroidX 团队报告缓存体积减少 3.75 倍。8.11 引入了并行加载和存储进一步缩短缓存读写时间。这些渐进式改进让配置缓存在 8.x 时代逐步成为可生产使用的特性为 9.0 的首选执行模式奠定了基础。buildSrc 的进化buildSrc 是 Gradle 组织构建逻辑的特殊项目它的代码会被自动编译并加入构建脚本的 classpath。8.0 对 buildSrc 做了多项改进让它更接近普通 included build 的行为。最实用的改进是 buildSrc 的任务可以直接从命令行运行gradle buildSrc:build gradle buildSrc:test之前要运行 buildSrc 的测试很麻烦现在和普通项目一样。8.0 还允许 buildSrc 包含其他构建在 buildSrc/settings.gradle 中声明 includeBuild让构建逻辑的模块化更灵活。另外buildSrc 不再自动运行 test 任务只有真正需要 buildSrc 输出时才编译减少了不必要的开销。缓存清理与保留8.0 之前Gradle 用户主目录~/.gradle会无限增长缓存文件可能占用数十 GB 空间。8.0 引入了可配置的缓存清理和保留策略# gradle.properties org.gradle.cache.cleanuptrue// settings.gradlecacheCleanup{policyCleanupPolicy.ON_BUILD_COMPLETION everyDuration.ofDays(7)retention{files(DownloadedFiles.ALL){maxAgeDuration.ofDays(30)}}}默认保留 7 天未使用的缓存超过则清理。这个特性对开发机器和 CI 都很友好避免磁盘被缓存撑爆。Java Toolchains 改进8.0 对工具链做了多项增强。最实用的是工具链供应商匹配可以指定只使用特定厂商的 JDKjava{toolchain{languageVersionJavaLanguageVersion.of(17)vendorJvmVendorSpec.ADOPTIUM}}8.8 引入了 Daemon JVM 工具链允许 Gradle Daemon 运行在与 CLI 不同的 JVM 上。8.13 进一步支持 Daemon JVM 自动配置本地找不到匹配 JDK 时自动下载。8.14 支持 GraalVM 工具链能选择支持 Native Image 的 JDK。这些改进让 Gradle 在多 JDK 环境下的行为更可预测对需要在不同 Java 版本间切换的项目特别有用。编译器守护进程保活8.3 引入了 Java 编译器守护进程保活在 Linux 和 macOS 上把编译时间缩短最多 30%。之前每次编译任务都会启动新的 javac 进程保活后编译器进程在构建之间保持存活避免重复启动开销。8.4 把这个特性扩展到 Windows。这个特性是自动启用的无需配置。对于以 Java 编译为主的大型项目如 Spring 框架本身效果立竿见影。Gradle 9面向未来的构建范式配置缓存成为首选执行模式Gradle 9.0.0 于 2025 年 7 月发布最重要的变化是配置缓存成为首选执行模式。虽然还没有默认启用但 Gradle 会在构建结束时主动建议启用并在 gradle init 生成的新项目中默认开启。9.0 的目标是让配置缓存在 10.0 成为默认且唯一的执行模式。9.0 引入了优雅降级机制当遇到不兼容配置缓存的插件或特性时Gradle 会自动回退到传统模式而不是直接失败。这包括 Maven Publish、Ivy Publish 等核心插件的部分功能以及 Eclipse、IDEA 等 IDE 插件。降级原因会写入配置缓存报告方便排查。# gradle.properties org.gradle.configuration-cachetrue # 如果不想看到启用提示可以显式关闭 # org.gradle.configuration-cachefalse9.0 还清理了大量与配置缓存不兼容的废弃 API强制插件作者迁移到安全替代方案。短期内这可能导致一些老插件无法使用但长期看是必要的阵痛。Java 17 与 Kotlin 2 / Groovy 49.0 要求 Java 17 或更高版本来运行 Gradle Daemon。这是一个重大变化许多仍在用 Java 8 或 11 的项目需要先升级运行环境。需要注意的是这个要求只针对运行 Gradle 本身编译和测试代码仍然可以用工具链指定更老的 Java 版本。java{toolchain{languageVersionJavaLanguageVersion.of(8)// 编译目标可以是 Java 8}}9.0 嵌入了 Kotlin 2.2.x 运行时使用 Kotlin 语言版本 2.2。这意味着构建脚本和插件可以用上 K2 编译器和 Kotlin 2 的新特性。同时 Groovy 升级到 4.0带来新的语言特性和性能改进。这两个升级主要影响插件作者普通用户的构建脚本通常不需要改动。语义化版本控制从 9.0 开始Gradle 采用语义化版本控制SemVer版本号格式为 MAJOR.MINOR.PATCH。之前的小版本号省略 PATCH 段如 8.5 而不是 8.5.09.0 之后所有新版本都遵循 SemVer。这个变化对用户来说意味着版本号更规范自动化脚本解析版本号更可靠。# Gradle 8.x 写法./gradlew wrapper --gradle-version8.5# Gradle 9.x 写法./gradlew wrapper --gradle-version9.0.0另外标记为 Incubating 的 API 不再被视为公共 API 的一部分可能在次要版本中变更。这给了 Gradle 团队更多迭代空间也提醒用户谨慎依赖孵化期 API。Kotlin DSL 编译避免9.0 对 Kotlin DSL 的脚本编译避免做了重大改进。之前 Gradle 用内部机制检测构建逻辑的 ABI 变化对内联函数等场景处理不佳。9.0 改用 Kotlin 内置的 ABI 指纹检测精度大幅提升。对于 Gradle 自身的构建非 ABI 变更的构建逻辑修改可以让配置时间减少最多 60%。这意味着你修改一个注释或方法体不会触发所有 Kotlin DSL 脚本的重新编译。这个改进对大型 Kotlin DSL 项目特别友好。可重现的归档输出9.0 让归档任务jar、zip 等的输出默认可重现。之前归档文件中条目的顺序可能因文件系统而异导致同样的输入产生不同的输出影响构建缓存命中。9.0 之后归档顺序固定按字典序时间戳固定确保可重现构建。tasks.named(jar){reproducibleFileOrdertruepreserveFileTimestampsfalse}这个特性对发布到 Maven 仓库的库特别重要可重现的产物让下游的构建缓存更有效。升级建议与实践版本选择策略对于新项目直接使用最新的 Gradle 9.x 是合理选择能享受配置缓存、Kotlin 2、更好的性能等所有现代特性。新项目没有历史包袱gradle init 生成的脚手架已经默认启用配置缓存。对于存量项目建议按版本逐步升级不要一次跨越多个大版本。每次升级前先运行gradle help --scan生成构建扫描报告查看废弃 API 的使用情况。升级路径通常是 6.x → 7.x → 8.x → 9.x每个大版本先升级到该系列的最后一个次要版本如 7.6、8.14再升级到下一个大版本。配置缓存迁移配置缓存是 9.0 的核心建议尽早迁移。迁移步骤先在 gradle.properties 中启用org.gradle.configuration-cache.problemswarn运行常用任务查看不兼容警告逐个修复不兼容的插件和自定义任务最后把 problems 改为 fail确保严格兼容。常见的不兼容问题包括在任务执行阶段访问 Project 对象、使用外部全局状态、任务闭包捕获不可序列化的对象。修复方式通常是把这些状态作为任务输入声明或改用 Provider 和 Property API。依赖管理现代化把传统的 ext 版本声明或 dependencies.gradle 文件迁移到版本目录。版本目录是 7.0 引入、8.0 稳定的特性是当前推荐的依赖管理方式。迁移后所有依赖坐标集中在 libs.versions.toml 文件中IDE 支持良好团队协作更顺畅。同时检查 implementation 和 api 的使用是否合理。把不必要的 api 改为 implementation可以减少传递依赖的暴露加快下游编译速度。这个区分从 6.0 开始被强调是依赖卫生的重要实践。工具链与 Wrapper为所有项目配置 Java 工具链避免依赖开发机器的 JDK 版本。工具链从 7.0 引入9.0 已经非常成熟支持自动下载、vendor 匹配、GraalVM 等高级特性。始终使用 Wrapper 提交 Gradle 版本到版本控制团队成员和 CI 都用相同的版本。升级时通过./gradlew wrapper --gradle-versionx.y.z命令更新不要手动修改 gradle-wrapper.properties。9.0 之后版本号遵循 SemVer记得写完整的 x.y.z 形式。构建工具的演进从未停止Gradle 10 已经在酝酿中配置缓存默认启用、更激进的并行执行、更智能的增量检测都在路线图上。理解每个版本的演进逻辑不仅能让你更好地使用当前版本也能在未来升级时快速适应。构建是一门手艺而掌握工具的演进史是成为优秀构建工程师的必经之路。