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 有多个不
   123   124   125   126   127   128   129   130   131   132   133