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

3050                                                       软件学报  2025  年第  36  卷第  7  期


                 同的依赖范围, 而不同依赖范围之间的误用造成的危害也不尽相同. 因此, 此类异味有大量触发场景和对应的危
                 害. 我们分别选取     Maven  和  Gradle 中主要的依赖范围, 将不同依赖范围之间误用所对应的危害列于表                    5  和表  6
                 中, 其中, 表行代表依赖库的预期依赖范围, 表列代表实际依赖范围, 表的内容是出现实际和预期依赖范围不一致
                 时可能造成的后果. 从表中可以看出, 依赖范围误用可能造成许多种类的危害. 接下来我们将结合实例, 分别举例
                 介绍依赖范围误用对模块自身运行和下游模块构建可能造成的危害和对应的具体触发场景.


                                   import lib2.A
                                   A classA = new  mod 直接使用传递依赖 lib2 中的类 A
                                   A();           但未在配置文件中声明
                                    包含代码
                                                                      lib2.A
                                      依赖于        依赖于        包含类
                                                                   public class A {
                                                                     ...
                                                                   }
                                 模块 mod  依赖库 lib1: 4.0.0  依赖库 lib2
                                                不再依赖 lib2
                                        升级 lib1
                                                               mod 在 lib1 升级后无法获取 lib2  构建错误
                               (a) 类型 1.4  依赖库 lib1: 4.0.1
                                         依赖于            包含类        lib.A
                                                               public class A {
                                                                  ...
                                 模块 mod      依赖库 lib: compileOnly  }
                                    包含代码
                                                compileOnly 代表 lib
                                 import lib.A    不会在运行时出现
                                 A classA = new A();                              运行时错误
                               (b) 类型 1.6              但 lib 中的 A 会在运行时被使用
                                         依赖于            依赖于               implementation 代表 lib
                                                                         不会在 client 构建时出现
                               下游模块 client      模块 mod    依赖库 lib: implementation
                                    包含类            包含类           包含类
                                    client.C        mod.B         lib.A
                                import mod.B   import lib1.A   public class A {
                                public class C extends B {   public class B extends A {   ...
                                   ...            ...          }  但由于类 A 是类 C 的父类,
                                }              }                                    下游模块
                                                               client 构建时需要 lib 中的类 A  构建错误
                               (c) 类型 1.6
                                        依赖于          依赖于          runtime 代表 lib2
                                                                应该在 lib1 的运行时出现
                                 模块 mod  依赖于  依赖库 lib1  依赖库 lib2: runtime
                                                           但由于依赖范围 runtime 被 test 覆盖,  运行时错误
                               (d) 类型 1.7   依赖库 lib2: test   lib2 最终只会在测试时出现
                                                图 4 造成危害的依赖异味案例



                                          表 5 Maven  中依赖范围误用的触发场景和危害

                                                                 预期范围
                              实际范围
                                            compile       runtime       provided       test
                              compile         -            2, 7          5, 9         2, 5, 7, 9
                              runtime          1            -             1            5, 9
                              provided       3, 6, 8       3, 8           -             2
                                test           1           3, 8           1             -


                                          表 6 Gradle 中依赖范围误用的触发场景和危害

                                                                预期范围
                             实际范围
                                          api    implementation  runtimeOnly  compileOnly  test
                               api        -          7            2, 7        5, 9      2, 5, 7, 9
                           implementation  6         -            2, 7        5, 9      2, 5, 7, 9
                            runtimeOnly    1         1            -            1         5, 9
                            compileOnly   3, 8       3, 8         3, 8        -           2
                               test        1         1            3, 8         1          -
   124   125   126   127   128   129   130   131   132   133   134