Page 127 - 《软件学报》2025年第7期
P. 127
3048 软件学报 2025 年第 36 卷第 7 期
Maven 中的依赖范围误用表现形式较为单一. 在 9 个 Maven 依赖范围误用的问题报告中, 有 8 例中依赖库的预期
依赖范围为 test, 而实际依赖范围为 compile.
(7) 类别 1.7 依赖范围冲突: 直接依赖在依赖树中存在冲突的依赖范围 (2/47). 依赖库同时作为模块的直接依
赖和间接依赖存在, 但直接依赖对应的依赖范围与间接依赖对应的依赖范围不一致. 在图 3(b) 对应的#DW-3769 [33]
中, 模块导入 dropwizard:dropwizard-dependencies 中引入的依赖库 jakarta:jakarta.xml.bind-api:2.32 和 jakarta:jakarta.
xml.bind-api:2.32 为 test 范围的直接依赖. 然而, 这两个依赖库在传递依赖中同样存在, 并且它们的依赖范围被设
置为 compile. 这造成了依赖范围的冲突, 导致直接依赖中的测试范围覆盖了传递依赖中的 compile 范围. 因此, 尽
管这两个依赖库预期在构建、运行和测试阶段都被使用, 但最终只在测试阶段中被引入使用. 最终开发者将直接
依赖的范围同样设置为 compile 以解决冲突.
这类异味仅存在于 Maven 中, 其产生原因在于 Maven 的内部结构中隐藏的一个深层漏洞#MNG-8041 [34] . 相
比之下, Gradle 并不受此类漏洞的影响, 因此不会产生类似的异味. 这个 Maven 中的缺陷广泛存在于其所有的 3.x
版本中, 其结果是导致项目在处理依赖库冲突时无法正常运作. 值得注意的是, 这类异味很容易被忽略, 因为在将
出现此异味的项目作为库使用时, 依赖范围冲突并不会立即显现出问题; 只有当模块自身作为应用程序被使用时,
才会出现相应危害.
(8) 类别 1.8 依赖树冲突: Maven 和 Gradle 解析出的依赖树不一致 (1/47). 模块同时包含 Maven 和 Gradle 的配
置文件, 且 Maven 和 Gradle 根据各自配置文件解析出的依赖树不一致. 在图 3(c) 对应的#MSJC-192 [35] 中, 模块
Msgraph-sdk-java-core 同时使用 Maven 和 Gradle 进行依赖管理, 然而在这两个构建工具对应的配置文件中, 对依
赖库 com.azure:azure-identity 和 com.google.guava:guava 的版本声明却不一致. 最终开发者将 Maven 配置文件中
的版本配置调整为与 Gradle 一致, 并且引入自动化机器人以确保 Maven 中依赖版本保持与 Gradle 中一致.
这类异味仅存在于同时使用 Maven 和 Gradle 作为构建工具的项目中, 其产生原因主要在于开发者对项目配
置文件的修改未能同步至所有相关的构建工具对应的配置文件中. 以模块 Msgraph-sdk-java-core 为例, 其选择使
用 Gradle 进行依赖管理, 但也同时维护了 Maven 的配置文件, 以确保项目能够兼容不同的环境. 在 MSJC-176 [36]
中, 项目中对依赖库 guava 进行升级, 但仅在 Gradle 对应的配置文件 build.gradle 中进行了修改, 而并未将修改同
步至 Maven 对应的 pom.xml, 造成了 Maven 和 Gradle 依赖树的冲突. 最终在 MSJC-192 [35] 中, 开发者发现并修复
了此问题.
2) 项目粒度
在项目级别, 我们发现了 11 个 (占比 11/47=23.4%) 问题报告中的 5 种 (占比 5/13=38.5%) 依赖异味类别, 它
们分别对应表 3 中的类别 2.1–2.5. 为了完整地进行分类, 我们基于 Maven 和 Gradle 为项目管理提供的手段进行
考虑, 并关注其中涉及的依赖管理的具体实体 (如构建工具封装器). 最终我们总结出 5 类异味, 它们涵盖了 11 个
问题报告, 并且都同时存在于 Maven 和 Gradle 中. 接下来, 我们将给出异味 2.1–2.5 的定义. 由于篇幅限制, 我们将
对其中异味类别 2.2 和 2.3 进行详细介绍 (其他异味类别的详细分析也参见附录 A), 它们都是先前研究未涉及或
未充分讨论的, 并且分别对应了不同的构建工具.
(1) 类别 2.1 构建工具配置缺失: 项目中缺少对其所使用构建工具的配置 (2/47). 项目中缺少所使用的构建工
具的版本等关键信息的配置文件.
(2) 类别 2.2 构建工具封装器 JAR 缺失: 项目构建工具封装器对应的 JAR 包缺失 (6/47). 项目中所使用的构建
工具封装器所需的 JAR 包未出现在封装器对应路径下. 在图 3(d) 对应的#PENNA-16 [37] 中, 项目 Penna 在上传至
GitHub 时没有将 gradle-wrapper.jar 与项目的其他代码一起上传.gradle-wrapper.jar 是 Gradle 封装器的一部分, 它
用于自动下载和配置 Gradle 的特定版本, 以确保项目的构建与特定版本的 Gradle 兼容. 由于 gradle-wrapper.jar 的
缺失, 因此在尝试通过封装器脚本 gradlew 启动 Gradle 时, 脚本无法找到 gradle-wrapper.jar, 最终导致了 ClassNot
FoundException 异常. 开发者通过上传对应的 gradle-wrapper.jar 解决了此问题.
这类异味在 Maven 和 Gradle 中均存在, 其出现的主要原因是 JAR 包是二进制文件, 在版本控制系统的管理
中通常会忽略二进制文件, 因此构建工具封装器 JAR 更容易缺失. 以#LEGAL-570 [38] 为例, ASF 项目中允许源代码

