3、构建脚本结构:settings.gradle.kts、build.gradle.kts、init.gradle.kts

说实话,刚接触 Gradle 的人,很容易被这三个文件搞晕。我当年第一次看到项目里同时躺着三个 .kts 文件时,心里就一个想法:到底哪个才是入口?

其实没那么复杂。这三个文件各管一摊事,分工非常明确。咱们一个一个说。

3.1 settings.gradle.kts:项目的“总指挥”

这个文件是 Gradle 构建的起点。你想想看,Gradle 启动后第一件事是什么?它得先知道:我要构建哪些模块?

这就是 settings.gradle.kts 的职责。它定义了一个多模块项目的结构。

// settings.gradle.kts
rootProject.name = "my-awesome-app"

include(":core")
include(":web")
include(":api")

这里我特别想提醒一点:模块名前面的冒号不是装饰。它代表相对于根项目的路径。比如 :core 对应的是 core/ 目录。

我的习惯: 我会在 settings.gradle.kts 里也放一些插件管理逻辑。比如统一指定插件版本,这样所有子模块就不用重复写了。

另外,settings.gradle.kts 里还可以配置依赖解析策略。我在一个大型微服务项目里遇到过这样的坑:不同子模块依赖了同一个库的不同版本,结果运行时各种 ClassNotFoundException。后来我在 settings 里统一做了版本锁定,问题才解决。

// 统一版本管理
dependencyResolutionManagement {
    repositories {
        mavenCentral()
    }
    versionCatalogs {
        create("libs") {
            from(files("gradle/libs.versions.toml"))
        }
    }
}

3.2 build.gradle.kts:模块的“施工图”

每个模块都有自己的 build.gradle.kts。它描述了这个模块怎么构建:用什么插件、依赖哪些库、打什么类型的包。

说白了,这就是你告诉 Gradle:“我要把这个 Kotlin 源码变成可运行的 JAR 包,中间帮我做编译、测试、打包。”

// app/build.gradle.kts
plugins {
    id("org.jetbrains.kotlin.jvm") version "1.9.0"
    application
}

dependencies {
    implementation(project(":core"))
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
    testImplementation(kotlin("test"))
}

application {
    mainClass.set("com.example.MainKt")
}

这里有个细节:implementationapi 的区别。我刚开始用 Gradle 时,所有依赖全用 implementation,结果发现子模块引用不到某些类。后来才明白:api 会把依赖暴露给消费者,implementation 不会。

注意: 不要滥用 api。过度暴露依赖会导致编译时间变长,而且容易造成依赖冲突。我的原则是:除非子模块确实需要,否则一律用 implementation

3.3 init.gradle.kts:全局的“初始化脚本”

这个文件比较特殊。它不放在项目目录里,而是放在 ~/.gradle/init.gradle.kts 或者 GRADLE_USER_HOME 下。

它的作用是什么?说白了,就是给所有 Gradle 构建加一层“全局配置”。比如统一设置代理、配置仓库镜像、添加全局插件。

// ~/.gradle/init.gradle.kts
allprojects {
    repositories {
        maven {
            url = uri("https://maven.aliyun.com/repository/public")
        }
    }
}

我曾经在一个公司里,所有开发者的机器上都配了这么一个 init 脚本。这样大家拉下来的项目不用改任何配置,就能用内部仓库下载依赖。省了不少事。

但要注意:init.gradle.kts 不要滥用。它会影响所有构建,包括你从 GitHub 上 clone 下来的别人的项目。万一你在里面配了什么奇怪的逻辑,别人的项目可能就构建失败了。

一句话总结:
  • settings.gradle.kts —— 管“有哪些模块”
  • build.gradle.kts —— 管“每个模块怎么构建”
  • init.gradle.kts —— 管“全局环境配置”

3.4 三者的协作关系

这三个文件不是孤立的。它们有明确的执行顺序:

  1. Gradle 启动,先找 init.gradle.kts,执行全局配置
  2. 然后找 settings.gradle.kts,确定项目结构
  3. 最后根据 settings 找到各个模块,执行各自的 build.gradle.kts

这个顺序很重要。比如你在 init 里配置了仓库镜像,那 settings 和 build 里引用的仓库就会走镜像。如果你在 settings 里配置了插件版本,那 build 里引用插件时就不用再写版本号了。

下面这张图可以帮你理清它们的关系:

Gradle 构建脚本执行流程 init.gradle.kts 全局环境配置(仓库镜像、代理等) settings.gradle.kts 项目结构定义(模块列表、版本目录) :core/build.gradle.kts 核心模块构建 :web/build.gradle.kts Web模块构建 :api/build.gradle.kts API模块构建 执行顺序:自上而下,逐层推进

3.5 一些实用建议

最后,分享几个我在实际项目里积累的经验:

  • settings.gradle.kts 里尽量少写逻辑。 它只负责项目结构和仓库配置,别在里面写 task 定义或者复杂的条件判断。我见过有人把构建逻辑塞进 settings,结果调试起来非常痛苦。
  • build.gradle.kts 保持简洁。 如果构建逻辑复杂了,抽到 buildSrc 或者自定义插件里去。一个文件超过 200 行,就该考虑重构了。
  • init.gradle.kts 只放“全局必须”的配置。 比如公司内部仓库镜像、统一的插件版本。别放跟具体项目相关的逻辑。
  • 善用 version catalog。gradle/libs.versions.toml 里统一管理依赖版本,然后在 settings 里引用。这样所有模块的依赖版本都一目了然。
一个小技巧: 如果你不确定某个配置该放哪个文件,问自己一个问题:“这个配置是只对当前模块有效,还是对所有模块都有效?” 前者放 build.gradle.kts,后者放 settings.gradle.kts 或 init.gradle.kts。

嗯,这三个文件的关系搞清楚了,Gradle 构建脚本的基本结构你就掌握了八成。剩下的就是在实践中慢慢积累经验了。


公众号:蓝海资料掘金营,微信deep3321