Page 130 - 《软件学报》2025年第7期
P. 130

孙伟杰 等: Java 依赖异味的实证研究与统一检测技术                                                    3051


                    (1) 运行时错误: 触发场景为依赖库预期依赖范围是               implementation, 而实际依赖范围是   compileOnly. 某依赖
                 库的预期依赖范围是       implementation, 这意味着在真实情况下, 它不仅参与构建过程, 还应被包含在运行时环境中.
                 然而, 如果该库的实际依赖范围被设置为             compileOnly, 这就意味着它仅在构建阶段被调用, 而不会出现在运行时
                 出现. 这种配置错误会导致在运行时无法访问该依赖库的功能, 从而引发运行时错误. 在图                             4(b) 对应的#PMTS-
                 24 [41] 中, 模块  Payment-service 将依赖库  commons-codec:commons-codec 的依赖范围设置为  compileOnly, 但事实上
                 此依赖库在构建和运行时都被使用, 其预期范围应为                 implementation. 此时, 项目可以正常进行构建, 但是在运行时
                 试图访问   commons-codec:commons-codec 中的类  DigestUtils 时出现异常  NoClassDefFoundError.
                    (2) 下游模块构建错误: 触发场景为下游模块构建时使用依赖库中特性, 且依赖库预期依赖范围是                             api, 而实际
                 依赖范围是    implementation. 具体来说, 某依赖库的预期依赖范围是         api, 这意味着它不仅在构建过程中被使用, 还
                 会作为传递依赖被引入到所有依赖它的下游模块中. 然而, 如果该库的实际依赖范围被设置为                              implementation, 这
                 就意味着它不会作为传递依赖被引入下游模块的构建中. 这种配置错误会导致下游模块在构建时无法访问依赖库
                 的特性, 从而引发构建错误. 在图         4(c) 对应的#JAVACLIENT-2019  [42] 中, 模块  Java-client 将依赖  org.seleniumhq.
                 selenium:selenium-support:5.0  的依赖范围设置为  implementation. 对于使用  Java-client 的下游模块来说, 这个范围
                 意味着   selenium-support 不会作为传递依赖被引入     client 的构建中. 然而, 由于  Java-client 中的类  AppiumFluentWait
                 继承了   selenium-support 中的类  FluentWait, 下游模块  client 在试图使用  AppiumFluentWait 时也必须能够访达
                 FluentWait, selenium-support 的预期范围应该是  api. 此时, 由于依赖库  selenium-support 中的  FluentWait 未被引入,
                 下游模块构建错误.
                    3) 类别  1.7 依赖范围冲突: 直接依赖在依赖树中存在冲突的依赖范围                 (2/47). 与依赖范围误用类似, 不同依赖
                 范围之间的冲突可能造成不同的危害. 我们将最终结果展示在表                     7  中, 其中, 表列代表直接依赖的依赖范围, 表行
                 代表被覆盖的传递依赖的依赖范围, 表的内容是对应依赖范围出现冲突时的后果. 可以看到, 与依赖范围误用不
                 同, 依赖范围冲突不会对下游模块造成危害, 因为              Maven  中的漏洞仅会影响模块自身依赖解析. 此外, 从表格的右
                 上部分可以看出, 当直接依赖的使用场景包含了传递依赖的使用场景时也不会造成危害. 但依赖范围冲突同样可
                 能对模块自身造成危害. 接下来我们将结合实例, 介绍依赖范围冲突引发的运行时错误问题.

                                          表 7 Maven 中依赖范围冲突的触发场景和危害

                                                                 传递范围
                              直接范围
                                             compile       runtime       provided       test
                              compile          -             -             -             -
                               runtime         1             -              1            -
                              provided         3             3             -             -
                                test           1, 3          3              1            -

                    (1) 运行时错误: 触发场景为模块运行时使用依赖库中特性, 且依赖库作为直接依赖的依赖范围是                            test, 覆盖了
                 其作为传递依赖的依赖范围          runtime. 具体来说, 某依赖库的依赖范围被设置为          test, 这意味着该库仅在测试阶段被
                 引入. 然而, 如果该库在依赖树中同样以传递依赖形式存在, 其依赖范围为                     runtime, 这意味着引入此传递依赖的依
                 赖库在运行时需要该库的功能. 由于           test 范围会覆盖   runtime 范围, 最终导致该库仅在测试阶段被引入, 而不包含
                 在运行时环境中. 这种配置错误会导致模块在运行时无法访问依赖库的功能, 从而引发运行时错误. 在图                                 4(d) 对
                 应的以#COMMON-152    [43] 为例, 直接依赖  org.slf4j:slf4j-log4j12  的依赖范围被设置为  test, 但与此同时, 其在依赖树
                 中存在另一个依赖范围        runtime, 但由于  test 会覆盖  runtime, 最终导致  slf4j-log4j12  仅会在测试时被引入. 在这种
                 情况下, 模块运行时无法找到         slf4j-log4j12, 因此会导致项目运行时错误, 无法产生任何日志.
                    基于以上发现, 我们回答研究问题           RQ2  如下: 依赖异味可能在多种使用场景中被触发, 进而导致诸如构建错
                 误和运行时错误等       11  类危害. 更重要的是, 依赖异味的影响远不止于单一项目范畴, 它还可能在                    Java 生态系统内
                 部引发一系列连锁反应. 因此, 及时识别并解决依赖异味对于保障项目开发的质量与促进                            Java 生态系统的持续健
                 康发展至关重要.
   125   126   127   128   129   130   131   132   133   134   135