Page 128 - 《软件学报》2025年第7期
P. 128
孙伟杰 等: Java 依赖异味的实证研究与统一检测技术 3049
发布中包含许多类型的构建工具, 但 Maven 和 Gradle 的构建工具封装器 JAR 由于其二进制文件的形式而被禁止.
(3) 类别 2.3 构建工具封装器 JAR 异常: 项目构建工具封装器的 JAR 包实际校验和与官方提供的预期校验和
不一致 (1/47). 项目所采用构建工具封装器的 JAR 文件的实际二进制校验和与官方发布版本所声明的预期二进制
校验不符. 在图 3(e) 对应的#BISQ-3422 [39] 中, Bisq 开发者在升级 Gradle 版本至 5.6.4 后, 只修改了 Gradle 封装器
的版本配置, 而未更新 gradle-wrapper.jar 文件, 导致 gradle-wrapper.jar 的版本仍然是升级前的 4.10.2. 这样的操作
使得 gradle-wrapper.jar 的实际校验和与官方提供的 5.6.4 版本对应的预期校验和冲突. 其产生的主要原因为, 在进
行 Gradle 版本升级时, 第 1 次升级命令会更新 gradle-wrapper.properties 文件中的版本配置, 但不会更新实际的
JAR 文件. 第 2 次运行才会生成新的 JAR 文件, 使得实际的 JAR 文件与配置文件中指定的 Gradle 版本对应 JAR
一致. 最终开发者通过二次运行升级命令更新 JAR 文件, 解决了此问题.
这类异味在 Maven 和 Gradle 中均存在, 其主要有两种产生原因. 一种是上例中提到的未按照官方提出的两次
升级规范. 在这种情况下, 尽管 JAR 包的校验和与对应版本的官方校验和不一致, 但可能与其他版本的官方校验
和一致. 另一种情况是通过植入 JAR 包进行的攻击, 以一次针对 Minecraft Online 的攻击为例 [40] , 攻击者伪造了构
建工具封装器 JAR 并试图植入对应仓库, 以获取使用者的个人信息. 这类异味往往难以被发现, 因为 JAR 包以二
进制形式存在, 不使用特定工具难以确定替代 JAR 包的具体内容和来源. 因此, Maven 和 Gradle 发布了各个版本
的封装器 JAR 包的官方校验和, 并提供了利用校验和检验是否存在异常的功能.
(4) 类别 2.4 模块间库重复: 项目多个模块中包含未进行统一管理的同一依赖库 (2/47). 项目多个模块中使用
同一依赖库, 但并未使用构建工具提供的版本管理机制对其版本进行统一管理.
(5) 类别 2.5 模块间库冲突: 项目多个模块中使用了不同版本的同一依赖库 (1/47). 项目的多个模块中使用了
组织名和构件名都相同但版本不同的依赖库.
基于以上发现, 我们回答研究问题 RQ1 如下: 我们发现了 Java 项目中 13 种特征各异的依赖异味, 它们存在
于两大主流的项目构建工具 Maven 与 Gradle 中, 既包括单个模块内部的依赖库使用情况, 也触及了模块间的控
制, 体现了 Java 项目中依赖异味的多样性和普遍性.
2.2.2 RQ2: 这些依赖异味会在什么情境下产生怎样的危害?
为了全面分析这些依赖异味可能带来的危害, 我们从项目的整个生命周期进行了综合考察, 包括项目构建、
运行、产出和项目维护等. 此外, 我们不仅关注模块自身, 还考虑了其作为一个依赖库被下游模块使用时可能带来
的影响.
在研究中, 我们发现了 11 类可能的危害, 包括对模块自身以及下游模块的构建、运行等可能造成的危害. 详
细结果呈现在表 4 中. 这些危害可能由不同类别的依赖异味在不同的使用场景下被触发而引起. 针对每种危害, 我
们都能提供相应的问题报告或图示作为证明. 由于构建和运行是项目最重要的生命周期, 接下来我们将介绍结合
实际问题报告介绍异味 1.4、1.6 和 1.7 对模块自身和下游模块的构建和运行可能造成的危害以及对应触发场景.
1) 类别 1.4 未声明依赖: 模块直接使用了未在配置文件中声明的依赖库 (6/47). 模块中存在直接使用的依赖
库, 然而这些依赖库并未在项目的配置文件中得到明确声明. 这类异味可能导致构建错误、下游模块运行时错误
等一系列危害. 接下来我们将结合实际问题报告介绍这些危害的触发场景.
(1) 构建错误: 触发场景为模块构建时使用的依赖库在依赖树中不存在. 模块的源代码中使用了某依赖库, 但
这些依赖库既未在项目的配置文件中明确列出, 也未作为传递依赖被引入. 因此, 在构建过程中, 由于无法定位到
这些依赖库而导致构建错误. 在图 4(a) 对应的#EL-1849 中, 项目 Jersey 没有明确声明对依赖库 eclipse:asm:9.4.0
[5]
的依赖关系, 但通过 eclipse:moxy:4.0.0 间接引入, 从而能够访问 asm 中的类以进行构建和运行. 然而, 在 moxy 版
本升级到 4.0.1 后, moxy 不再依赖于 asm, 导致 Jersey 无法通过传递依赖间接访问 asm, 从而导致构建错误. 未声
明依赖时常难以被发现, 这主要因为虽然模块未直接声明使用的依赖库, 但传递依赖中恰好存在, 于是便错误地依
赖于传递依赖进行正常使用. 由于此时构建工具并不知道模块对此依赖库的使用, 开发者也难以管理自身依赖, 而
是被引入的传递依赖控制. 而当传递依赖不再存在时, 则可能触发模块自身构建错误.
2) 类别 1.6 依赖范围误用: 依赖库的实际依赖范围与预期依赖范围不一致 (12/47). Maven 和 Gradle 有多个不

