https://docs.gradle.org/current/userguide/dependency_management.html
不包括传递依赖
尽管上一节显示了如何强制实施某个版本的可传递依赖项,但本节将排除作为完全删除可传递依赖项的一种方式。
与强制依赖版本类似,完全排除依赖需要有意识的决定。如果外部库没有它们而无法正常运行,则排除传递依赖关系可能会导致运行时错误。如果您使用排除,请确保您没有通过足够的测试覆盖率来利用不需要排除依赖的任何代码路径。
可以在声明的依赖项级别上排除传递性依赖项。排除通过属性组和/或模块作为键/值对拼出,如下面的示例所示。有关更多信息,请参见ModuleDependency.exclude(java.util.Map)。
例子81.排除特定依赖声明的传递依赖
GroovyKotlin
build.gradle
dependencies {
implementation('commons-beanutils:commons-beanutils:1.9.4') {
exclude group: 'commons-collections', module: 'commons-collections'
}
}
在此示例中,我们向commons-beanutils添加了一个依赖关系,但不包括传递依赖项commons-collections。在下面显示的代码中,我们仅使用beanutils库中的一种方法PropertyUtils.setSimpleProperty()。通过我们的测试覆盖范围进行验证,将这种方法用于现有的设置员不需要从commons-collection获得任何功能。
例子82.使用beanutils库中的工具
src/main/java/Main.java
import org.apache.commons.beanutils.PropertyUtils;
public class Main {
public static void main(String[] args) throws Exception {
Object person = new Person();
PropertyUtils.setSimpleProperty(person, "name", "Bart Simpson");
PropertyUtils.setSimpleProperty(person, "age", 38);
}
}
实际上,我们表示仅使用该库的一个子集,而该子集不需要commons-collection库。可以将其视为隐式定义了commons-beanutils本身未明确声明的功能变体。但是,这样做会增加破坏未经测试的代码路径的风险。
例如,这里我们使用setSimpleProperty()方法来修改Person类中setter定义的属性,效果很好。如果尝试设置类中不存在的属性,则应该在类Person上收到类似Unknown property的错误。但是,由于错误处理路径使用commons-collections中的类,所以我们现在得到的错误是NoClassDefFoundError:org / apache / commons / collections / FastHashMap。因此,如果我们的代码更具动态性,而我们忘记了充分涵盖错误情况,那么我们库的使用者可能会遇到意外错误。
这只是说明潜在陷阱的一个示例。实际上,较大的库或框架会带来大量的依赖关系。如果这些库未能分别声明功能,并且只能以“全有或全无”的方式使用,则排除可能是将库缩减为实际需要的功能集的有效方法。
从好的方面来说,与Maven相比,Gradle的排除处理考虑了整个依赖关系图。因此,如果库上有多个依赖项,则只有在所有依赖项都对它们达成一致的情况下才执行排除。例如,如果我们将opencsv作为另一个依赖项添加到上述项目中,而该依赖项还取决于commons-beanutils,则不再排除commons-collection,因为opencsv本身并不排除它。
例子83.仅当所有依赖项声明都同意排除项时,排除项才适用
GroovyKotlin
build.gradle
dependencies {
implementation('commons-beanutils:commons-beanutils:1.9.4') {
exclude group: 'commons-collections', module: 'commons-collections'
}
implementation 'com.opencsv:opencsv:4.6' // depends on 'commons-beanutils' without exclude and brings back 'commons-collections'
}
如果我们仍然希望排除commons-collection,因为commons-beanutils和opencsv的组合用法不需要它,我们也需要从opencsv的传递依赖项中排除它。
例子84.为多重依赖声明排除传递依赖
GroovyKotlin
build.gradle
dependencies {
implementation('commons-beanutils:commons-beanutils:1.9.4') {
exclude group: 'commons-collections', module: 'commons-collections'
}
implementation('com.opencsv:opencsv:4.6') {
exclude group: 'commons-collections', module: 'commons-collections'
}
}
从历史上看,排除项还用作解决某些依赖项管理系统不支持的其他问题的辅助工具。但是,Gradle提供了可能更适合解决特定用例的各种功能。您可以考虑研究以下功能:
更新或降级依赖性版本:如果依赖性版本冲突,通常最好通过依赖性约束来调整版本,而不是尝试用不需要的版本排除依赖性。
组件元数据规则:如果库的元数据明显错误,例如,如果库的元数据包含在编译时永远不需要的编译时相关性,则可能的解决方案是删除组件元数据规则中的相关性。这样,您便告诉Gradle不再需要两个模块之间的依赖关系(即元数据是错误的),因此不应考虑。如果要开发库,则必须注意该信息不会发布,因此有时排除是更好的选择。
解决互斥的依赖冲突:您经常看到用排除解决的另一种情况是,两个依赖不能一起使用,因为它们表示同一事物(相同功能)的两个实现。一个流行的示例是冲突的日志记录API实现(例如log4j和log4j-over-slf4j)或在不同版本中具有不同坐标的模块(例如com.google.collections和guava)。在这种情况下,如果Gradle不知道此信息,则建议通过组件元数据规则添加缺失的能力信息,如声明组件功能部分中所述。即使您正在开发库,而您的使用者也不得不再次解决冲突,将决定权交给库的最终使用者通常是正确的解决方案。 IE。作为库作者,您最终不必决定您的使用者最终使用哪种日志记录实现。