9、依赖管理:仓库配置与依赖声明
依赖管理,说白了就是 Gradle 帮你自动下载和管理第三方库。我刚开始用 Gradle 时,觉得这玩意儿不就是个「自动下载器」嘛?后来踩过几次坑才发现,这里面的门道还真不少。
今天咱们就聊聊仓库配置、依赖声明,还有依赖传递与排除。嗯,这些都是日常构建中绕不开的核心技能。
9.1 仓库配置:你的代码从哪里来?
Gradle 需要知道去哪里下载依赖。这个「去哪里」就是仓库配置。我个人习惯把仓库配置写在 repositories 块里,像这样:
repositories {
mavenCentral()
jcenter()
maven { url "https://maven.aliyun.com/repository/public" }
mavenLocal()
}
这里有几个常见的仓库:
| 仓库名称 | 说明 | 现状 |
|---|---|---|
| mavenCentral() | Maven 中央仓库,最权威 | 推荐使用,稳定可靠 |
| jcenter() | JFrog 维护的仓库 | 已停止更新,不建议新项目使用 |
| mavenLocal() | 本地 Maven 缓存 | 适合调试本地构建 |
| 私服(自定义) | 企业内部仓库 | 企业项目必备 |
私服配置稍微复杂一点,需要指定 URL 和认证信息:
repositories {
maven {
url "http://nexus.company.com/repository/maven-public/"
credentials {
username = "admin"
password = "password123"
}
}
}
我曾经在配置私服时忘记加认证信息,结果构建一直报 401 错误。排查了半天才发现是 credentials 没写对。嗯,这种低级错误大家别犯。
9.2 依赖声明:implementation vs api vs compileOnly
依赖声明是 Gradle 依赖管理的核心。你想想看,一个依赖到底该用哪种方式声明?这直接影响到编译速度和依赖传递。
先看个例子:
dependencies {
implementation 'com.google.guava:guava:31.1-jre'
api 'org.springframework:spring-core:5.3.20'
compileOnly 'org.projectlombok:lombok:1.18.24'
runtimeOnly 'mysql:mysql-connector-java:8.0.30'
testImplementation 'junit:junit:4.13.2'
}
这里我重点说说 implementation 和 api 的区别:
| 声明方式 | 对外暴露 | 编译速度 | 使用场景 |
|---|---|---|---|
| implementation | 不暴露 | 快 | 内部实现依赖 |
| api | 暴露给消费者 | 慢 | 公共 API 依赖 |
| compileOnly | 仅编译期 | 快 | 编译时需要,运行时不需要 |
| runtimeOnly | 仅运行时 | 快 | 运行时需要,编译时不需要 |
举个例子,假设你写了一个工具库:
// 你的工具库
dependencies {
implementation 'com.google.gson:gson:2.9.0' // 内部使用 Gson
api 'org.slf4j:slf4j-api:1.7.36' // 用户需要用到 SLF4J
}
这里 Gson 是你内部用的,用户不需要知道。而 SLF4J 是用户需要直接调用的,所以用 api 暴露出去。
9.3 依赖传递与排除
依赖传递是 Gradle 自动帮你做的事情。你引入一个库,它会把该库依赖的其他库也一起拉下来。听起来很方便对吧?但有时候也会带来麻烦。
我曾经遇到过一个经典问题:项目里同时引入了两个库,它们都依赖了不同版本的 Log4j。结果运行时出现了 ClassNotFoundException。嗯,这就是依赖冲突。
解决依赖冲突有几种方式:
- 强制指定版本:用
force = true - 排除传递依赖:用
exclude - 使用 BOM:统一管理版本
看代码:
dependencies {
implementation('com.example:library-a:1.0.0') {
exclude group: 'commons-logging', module: 'commons-logging'
}
implementation('com.example:library-b:2.0.0') {
transitive = false // 关闭传递依赖
}
implementation('com.example:library-c:3.0.0') {
force = true // 强制使用这个版本
}
}
gradle dependencies 命令可以查看完整的依赖树。我每次遇到依赖问题,第一件事就是跑这个命令。它能帮你快速定位冲突。
还有一种更优雅的方式——使用 BOM(Bill of Materials):
dependencies {
implementation platform('org.springframework.boot:spring-boot-dependencies:2.7.0')
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.google.guava:guava' // 版本由 BOM 统一管理
}
BOM 的好处是统一管理版本,避免手动指定。我个人非常推荐在大型项目中使用 BOM。
9.4 知识体系总览
下面这张图帮你梳理本章的核心内容:
exclude 排除了其中一个版本,问题才解决。所以,依赖管理不是小事,一定要重视。
好了,关于依赖管理的内容就聊到这里。记住:仓库配置要选对,依赖声明要谨慎,依赖传递要可控。这三条做好了,你的构建基本就稳了。
公众号:蓝海资料掘金营,微信deep3321