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 生态系统的持续健
康发展至关重要.

