生产者与消费者

Gradle 中依赖管理的一个关键概念是消费者和生产者之间的区别。

当您构建一个库时,您实际上处于生产者端:您正在生成工件,这些工件将被其他人(消费者消费

传统构建系统存在许多问题,原因在于它们没有区分生产者和消费者。

消费者需要从广义上理解

  • 依赖于另一个项目的项目是消费者

  • 依赖于工件的任务是粒度更细的消费者

在依赖管理中,我们做出的许多决策取决于我们正在构建的项目类型,也就是说,我们是什么类型的消费者

生产者变体

生产者可能希望为不同类型的消费者生成不同的工件:对于相同的源代码,会生成不同的二进制文件。或者,项目可能会生成供其他项目(同一存储库)使用的工件,但不用于外部使用。

Java 世界中的一个典型示例是 Guava 库,它以不同的版本发布:一个用于 Java 项目,另一个用于 Android 项目。

但是,由使用者负责告知要使用哪个版本,并且由依赖项管理引擎负责确保图的一致性(例如确保不会在类路径中同时出现 Guava 的 Java 和 Android 版本)。这是 Gradle 的变体模型发挥作用的地方。

在 Gradle 中,生产者变体通过可消耗配置公开。

强封装

为了让生产者编译库,它需要在编译类路径上拥有其所有实现依赖项。有些依赖项只是作为库的实现细节必需的,而有些库实际上是 API 的一部分。

但是,依赖于此生成库的库只需要“看到”库的公共 API,因此只需要此 API 的依赖项。这是生产者编译类路径的一个子集:这是依赖项的强封装。

结果是分配给库的implementation配置的依赖项不会出现在使用者的编译类路径中。另一方面,分配给库的api配置的依赖项会出现在使用者的编译类路径中。然而,在运行时,所有依赖项都是必需的。Gradle 甚至在单个项目中区分不同类型的使用者:例如,Java 编译任务与 Java exec 任务是不同的使用者。

有关 Java 世界中 API 和运行时依赖项分离的更多详细信息,可以在此处找到

尊重使用者

每当您作为开发人员决定包含依赖项时,您都必须明白,对您的使用者有影响。例如,如果您向项目添加依赖项,它将成为使用者的传递依赖项,因此如果使用者需要不同版本,则可能参与冲突解决。

Gradle 处理的许多问题都是关于修复使用者和生产者的期望不匹配。

但是,有些项目比其他项目更容易

  • 如果您处于消费链的末端,也就是说您构建了一个应用程序,那么您的项目实际上没有消费者(最终客户除外):添加排除项不会产生其他后果,只会解决您的问题。

  • 但是,如果您是一个库,添加排除项可能会阻止消费者正常工作,因为他们会执行您不执行的代码路径

请务必记住,您为解决问题而选择的解决方案可能会“泄露”给您的消费者。本文档旨在指导您为正确的问题找到正确的解决方案,更重要的是,做出有助于解析引擎在发生冲突时做出正确决策的决策。