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  则关
   150   151   152   153   154   155   156   157   158   159   160