Page 123 - 《软件学报》2025年第7期
P. 123
3044 软件学报 2025 年第 36 卷第 7 期
1.0.0 这个库. 值得注意的是, 项目 pr 的配置文件 pom.xml 中包含<dependencyManagement>标签, 用于统一管理依
赖版本. 这样一来, mod1 和 mod2 可以继承这个版本信息, 而不需要在各自的 pom.xml 文件中重复声明版本号.
为了确保 Java 项目在不同环境中的构建一致性和可重现性, Maven 和 Gradle 提供了构建工具封装器
(wrapper) 来启动相应版本的构建工具. 封装器包括启动脚本和 JAR 文件 (Maven 使用 maven-wrapper.jar, Gradle
使用 gradle-wrapper.jar). 在构建工具封装器正常工作的情况下, 开发者无须在本地安装 Maven 和 Gradle, 只需运
行脚本, 即可调用对应的 JAR 文件, 自动下载并使用相应版本的构建工具.
1.2 构建工具依赖解析机制
Maven 和 Gradle 极大地简化了依赖管理的复杂性. 开发者只需要在配置文件中对项目的直接依赖进行配置,
构建工具会对配置文件中声明的依赖库进行解析, 下载并管理所有的直接依赖和引入的间接依赖. 在解析过程中,
它们会遵循依赖仲裁 (dependency mediation) 机制来确保解析结果的一致性, 主要包括以下两点.
● 版本仲裁机制: 当依赖树中出现同一依赖库的不同版本时, Maven 和 Gradle 仅会选择其中一个版本, 并忽略
其他版本. Maven 使用“最短路径优先”策略, 优先选择依赖树中离模块最近的版本; 相比之下, Gradle 默认采用“最
高版本优先”策略, 在冲突时选择最高的版本. 例如, 在图 2(a) 中, 模块 mod 同时依赖于 lib:v1.0.0 和 lib:v2.0.0, 此
时 Maven 会选择距离较近的 lib:v1.0.0, 而 Gradle 则会选择版本较高的 lib:v2.0.0.
Maven Gradle
依赖于
选择 v1.0.0 选择 v2.0.0
依赖库 lib1: v1.0.0
模块 mod 依赖于
依赖于
(a) 依赖库 lib2 依赖库 lib1: v2.0.0
依赖于
依赖库 lib1: test
模块 mod 依赖于
依赖于
(b) 依赖库 lib2 依赖库 lib1: compile
图 2 构建工具解析机制
● 范围仲裁机制: 当依赖树中出现同一依赖库的不同依赖范围时, Maven 仅会选择直接依赖的范围而忽略传
递依赖的范围. 例如, 在图 2(b) 中, 依赖库 lib 既是直接依赖又是间接依赖, 其作为直接依赖的范围与作为传递依
赖的范围不一致, 最终传递依赖的范围会被忽略.
2 依赖异味的实证研究
为了深入研究 Java 项目依赖管理中潜藏的问题, 我们针对 Java 项目中可能存在的依赖异味及其影响开展实
证研究. 接下来我们将介绍数据收集流程并展示研究结果.
● RQ1: Java 项目中可能存在哪些依赖异味类别?
● RQ2: 这些依赖异味会在什么情境下产生怎样的危害?
为了回答 RQ1 和 RQ2, 我们选择了 3 类调研对象, 分别是官方文档、学术论文和开源社区. 我们从官方文档
和学术论文中总结出关键词, 根据关键词从开源社区中搜索问题报告, 在人工分析后最终筛选出 47 个依赖异味相
关问题报告. 下面我们将详细介绍数据收集过程和调研结果.
2.1 数据收集
步骤 1: 收集相关问题报告. 为了全面调研 Java 项目依赖管理中潜藏的问题, 我们选择了以下 3 类调研对象:
(1) 官方文档: Maven、Gradle 的依赖管理手册 [15,16] ; (2) 学术论文: 依赖管理相关的论文 [7−14,17−29] ; (3) 开源社区:

