跳到正文
Elaine Blog
返回

Maven、Gradle 与依赖冲突

Java 基础

构建工具的核心任务是把源码和一组确定输入变成可重复的产物。Maven 偏向固定约定与声明式模型,Gradle 提供更灵活的 任务模型;选择哪一个不如“版本是否固定、依赖图是否可解释”重要。

Table of contents

Open Table of contents

先读懂依赖坐标

Maven 坐标 groupId:artifactId:version 唯一定位一个构件,例如 org.junit.jupiter:junit-jupiter:6.0.1。**传递依赖(Transitive Dependency)**是直接依赖进一步引入的库。

运行以下命令观察实际依赖图:

mvn dependency:tree
mvn dependency:analyze

Maven 冲突时通常采用“路径最近”的版本;同深度时先声明者优先。不要依赖这个偶然顺序,直接使用的库应显式声明, 关键版本放入 dependencyManagement 或导入 BOM。Maven 的 依赖机制文档给出了完整规则。

Scope 决定类路径

compile 进入编译、测试和运行类路径;runtime 编译时不可见但运行需要;test 只用于测试;provided 表示运行环境 负责提供。Scope 填错可能让本地测试通过、部署后却出现 ClassNotFoundException

**BOM(Bill of Materials,物料清单)**是一份兼容版本集合。它管理版本,不会自动把全部依赖加入项目;业务模块仍要 声明自己直接使用的构件。

Gradle 的对应做法

Gradle 使用 implementationruntimeOnlytestImplementation 等配置表达可见范围,并可启用依赖锁定:

java {
    toolchain { languageVersion = JavaLanguageVersion.of(21) }
}

dependencyLocking {
    lockAllConfigurations()
}

生成锁文件:

./gradlew dependencies --write-locks

锁文件应提交到版本库。Gradle 官方的依赖锁定说明指出, 锁定会验证后续解析结果与保存状态一致。Java Toolchain 则让编译、测试和文档任务使用明确 JDK。

冲突排查顺序

  1. 记录运行时错误中的类名、方法签名和实际版本。
  2. dependency:treedependencyInsight 找到所有引入路径。
  3. 查库的兼容矩阵,不要直接排除看似重复的 JAR。
  4. 在统一版本平台中固定版本,并补一个能复现冲突的测试。
  5. 在 CI 重新解析依赖并构建,不复用开发机偶然缓存。

避免 LATEST1.+ 和普通发布中的 SNAPSHOT。还应使用内部制品代理、校验和与软件物料清单(SBOM)控制供应链风险。

下一步

阅读JUnit 与 Mockito 测试替身,让依赖边界可以被快速验证。


分享这篇文章:

上一篇
时间、金额、精度与序列化
下一篇
JUnit 与 Mockito:测试替身怎么选