依赖声明与仓库配置
这一章,我们来聊聊 Gradle 里最基础也最容易出错的两个东西:repositories 和 dependencies。说白了,就是告诉 Gradle:你去哪儿找包?你要什么包?
我刚开始用 Gradle 时,觉得这两块就是写几行代码的事。后来被坑了几次才明白,顺序、写法、仓库选择,每个细节都有可能让构建慢得像蜗牛,或者直接失败。嗯,咱们一个一个拆开说。
仓库配置 —— repositories 块
仓库就是依赖的“货源”。你声明了依赖,Gradle 会按仓库列表的顺序逐个去找。
先看看最常见的仓库配置长什么样:
repositories {
mavenCentral()
google()
maven { url 'https://jitpack.io' }
mavenLocal()
flatDir { dirs 'libs' }
}
这里有几个要点:
- mavenCentral() —— Maven Central,全球最大的 Java 仓库。我建议所有项目都至少配它。稳定,靠谱。
- google() —— Android 开发必备。存放 AndroidX、Material 等官方库。
- jcenter() —— 曾经很流行,但 2021 年已经停止服务。如果你的项目还在用,赶紧换掉。我记得有个老项目一直卡在构建上,查了半天发现是 jcenter 返回了 404 但 Gradle 还在傻等超时。从那以后我再也不用它了。
- maven { url '...' } —— 自定义 Maven 仓库,比如内部私服、JitPack、阿里云镜像。
- mavenLocal() —— 本地 ~/.m2 仓库。适合本地调试自己发布的包。
- flatDir —— 直接引用本地 jar 文件目录。我用得少,但有时接手遗留项目会看到。
下面这个表格可以帮大家快速选择:
| 仓库 | 适用场景 | 注意事项 |
|---|---|---|
| mavenCentral() | 几乎任何 Java/Kotlin 项目 | 速度一般,建议配镜像 |
| google() | Android 项目 | 必须配合 mavenCentral |
| mavenLocal() | 本地发布调试 | 只用于开发机,不要提交到 CI |
| 自定义 Maven | 公司私服、镜像、快照库 | 需要确保网络可达 |
| jcenter() | 放弃,不要再使用 | 已经只读,随时可能彻底关闭 |
| flatDir | 本地 libs 目录管理 | 不推荐大规模使用 |
依赖声明 —— dependencies 块
仓库是“超市”,依赖就是你的“购物清单”。Gradle 支持多种依赖坐标写法,但最常用的是「组:名称:版本」这种三要素格式。
dependencies {
implementation 'com.google.guava:guava:33.0.0-jre'
testImplementation 'junit:junit:4.13.2'
compileOnly 'org.projectlombok:lombok:1.18.30'
runtimeOnly 'ch.qos.logback:logback-classic:1.4.11'
implementation project(':core')
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
这里每个配置(configuration)都有讲究:
- implementation —— 最常用。依赖对模块外部不可见,编译更快。我建议新项目默认都用它。
- api —— 会暴露给消费者。如果你的库把某个依赖当作公开 API 的一部分,用 api 而不是 implementation。
- compileOnly —— 只在编译时需要,运行时不用。典型例子:Lombok、注解处理器。
- runtimeOnly —— 编译不需要,运行时必须有。比如某个数据库驱动。
- testImplementation —— 测试代码专用。JUnit、Mockito 放这里。
- project() —— 依赖同一项目的另一个模块。
- fileTree —— 依赖某个文件夹下的所有 jar。偶尔有用。
依赖管理与版本统一
项目大了,同一个依赖可能在多个模块出现。版本写得到处是,改起来很痛苦。Gradle 给了我们几个好工具。
1. BOM(Bill of Materials)
用 Spring Boot 或 Jackson 等库时,可以直接导入它们的 BOM,省去自己写版本。
implementation platform('org.springframework.boot:spring-boot-dependencies:3.2.0')
implementation 'org.springframework.boot:spring-boot-starter-web'
// 版本由 BOM 统一管理,无需写版本号
2. Version Catalog(Gradle 7.0+)
Gradle 官方推荐的方式。在 gradle/libs.versions.toml 里集中定义版本和依赖,然后在 build.gradle 里引用。
// libs.versions.toml
[versions]
guava = "33.0.0-jre"
[libraries]
guava = { group = "com.google.guava", name = "guava", version.ref = "guava" }
// build.gradle
dependencies {
implementation libs.guava
}
为什么我喜欢 Version Catalog?它让依赖的改动变得特别清晰,而且 IDE 会自动补全。你想想看,一个 20 模块的项目,要在每个 build.gradle 里改同一个版本号有多容易出错。用 catalog 后,我只改一行。
避坑指南(都是血泪史)
我在项目中踩过不少坑,挑几个典型的说说:
- 仓库顺序导致构建缓慢。 我曾经在 build.gradle 里把
maven { url 'http://slow-mirror.com' }写在了 mavenCentral 前面,结果每次构建都要等镜像超时(足足 30 秒)才去中央仓库。后来我把快的放前面,构建时间直接砍半。 - 漏写仓库。 有次我引入了一个第三方库,但它依赖的某个 transitive 依赖只存在于 JitPack。我没配 JitPack 仓库,Gradle 找半天找不到,最后抛异常。排查了半天才发现… 嗯,从那以后我写依赖之前会先看一眼它的文档里强调了哪个仓库。
- implementation 误用成 api。 上面提到了,编译时间暴涨。还有一个更隐蔽的问题:api 会让依赖泄露到外部,如果外部模块依赖了你内部的一个私有库的版本,就会出现莫名其妙的冲突。我帮同事 debug 过这个,最后把 api 改成 implementation 就解决了。
- 版本号写死 vs 动态版本。 有人喜欢用
30.+这种动态版本号,觉得省事。但动态版本每次构建都可能解析到不同版本,导致“昨天能构建今天就不能”的玄学问题。我个人的习惯是:除了开发初期,一律用固定版本。
以上就是依赖声明与仓库配置的核心内容。记住,好的配置习惯能让你少熬夜 debug。下次遇到依赖问题,先检查仓库顺序,再检查依赖配置,大概率能搞定。