Page 155 - 《软件学报》2025年第7期
P. 155
3076 软件学报 2025 年第 36 卷第 7 期
形式获取 lib 中的类 lib.A, 其预期范围应该是 runtime. 由于 lib.A 以反射形式被引入, 此时 mod 可以正常进行构
建, 但当下游项目运行时使用 runMtdLib 时, 则会因为 lib 未在运行时被引入, 无法找到 lib.A, 从而导致下游模块
运行时错误.
(a) 危害 1 lib.A
依赖于 包含类 public class A {
...
}
模块 mod 依赖库 lib: test 预期依赖范围: compile
包含 构建时 构建错误
构建时依赖库
import lib.A 由于 lib 的实际依赖范围是 test,
它在 mod 构建时不会出现
A classA = new A ();
依赖库 lib
(b) 危害 2
依赖于
预期依赖范围: test 构建成本增加
模块 mod 依赖库 lib: compile
构建时
构建时依赖库 mod 构建时由于 lib 的依赖范围 compile 而下载 lib!
包括 但 lib 预期依赖范围是 test, 意味着 lib 事实上仅在测试时使用,构建时不用出现!
依赖库 lib
(c) 危害 3
依赖于 包含类 lib.A
public class A { 运行时错误
... 但事实上 lib1 应该出现在
模块 mod 依赖库 lib: compileOnly } 构建时和运行时
包含代码
import lib.A 实际异味范围 compileOnly 代表 lib
在 mod’s 运行时不会出现
A classA = new A ();
(d) 危害 5
依赖于
预期依赖范围: test 构建产物冗余
模块 mod 依赖库 lib: compile
当被打包为可执行构件时
由于预期依赖范围是 test, 代表事实上
可执行构件 打包时不应该被包括
包括 包括
模块 mod 依赖库 lib1
图 A8 异味 1.6 对当前模块的危害与对应触发场景
(6) 下游模块构建产物冗余: 以预期范围为 test, 实际范围为 compile 为例, 在图 A9(d) 中, 模块 mod 将直接依
赖 lib 的依赖范围设置为 compile, 但其实际上仅在测试时被使用, 预期依赖范围应为 test. 下游模块 client 在使用
模块 mod 的同时, 也引入了 mod 的依赖库 lib 作为传递依赖. 将 client 打包为可执行 JAR 时, 直接依赖和传递依赖
中依赖范围为 compile 的依赖库都会被包含, 但事实上 lib 仅在测试时使用而不需被打包, 这就导致下游模块 lib
构建产物冗余.
7) 类别 1.7 依赖范围冲突
与异味 1.6 依赖范围误用类似, 不同依赖范围之间的冲突也可能造成多样的危害. 我们将详细结果汇总于正
文表 7. 下文将针对正文中未能详述的各类冲突可能造成的危害及其触发场景进行详细介绍.
(1) 构建错误: 其触发场景为直接依赖的 runtime 和 test 范围覆盖了传递依赖的 compile 和 provided 范围. 以
图 A10(a) 为例, 模块 mod 有直接依赖 lib1 和 lib2, 而 lib1 也引入 lib2 作为传递依赖. 在 mod 的依赖树中, lib2 作
为直接依赖的范围是 test, 而作为传递依赖的范围是 compile. 此时, lib2 的最终范围是 test, 仅会在测试时出现. 但
在 mod 中虽然并未直接引入 lib2 中的类, 但其中的类 C 以 lib2 中的类 A 为父类, 这就导致 mod 构建时无法找到
类 A, 进而出现构建错误.
8) 类别 1.8 依赖树冲突
依赖树冲突的危害与异味 1.3 类似, 其差别在于异味 1.3 关注于单一依赖树中的库版本冲突, 而异味 1.8 则关

