构建工具的核心任务是把源码和一组确定输入变成可重复的产物。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 使用 implementation、runtimeOnly、testImplementation 等配置表达可见范围,并可启用依赖锁定:
java {
toolchain { languageVersion = JavaLanguageVersion.of(21) }
}
dependencyLocking {
lockAllConfigurations()
}
生成锁文件:
./gradlew dependencies --write-locks
锁文件应提交到版本库。Gradle 官方的依赖锁定说明指出, 锁定会验证后续解析结果与保存状态一致。Java Toolchain 则让编译、测试和文档任务使用明确 JDK。
冲突排查顺序
- 记录运行时错误中的类名、方法签名和实际版本。
- 用
dependency:tree或dependencyInsight找到所有引入路径。 - 查库的兼容矩阵,不要直接排除看似重复的 JAR。
- 在统一版本平台中固定版本,并补一个能复现冲突的测试。
- 在 CI 重新解析依赖并构建,不复用开发机偶然缓存。
避免 LATEST、1.+ 和普通发布中的 SNAPSHOT。还应使用内部制品代理、校验和与软件物料清单(SBOM)控制供应链风险。
下一步
阅读JUnit 与 Mockito 测试替身,让依赖边界可以被快速验证。