在 Gradle 中,api 配置暴露了依赖关系,而实现却没有,这到底意味着啥?

Posted

技术标签:

【中文标题】在 Gradle 中,api 配置暴露了依赖关系,而实现却没有,这到底意味着啥?【英文标题】:What does it really mean that api configuration exposes depedencies whereas implementation does not, in Gradle?在 Gradle 中,api 配置暴露了依赖关系,而实现却没有,这到底意味着什么? 【发布时间】:2020-05-29 20:13:30 【问题描述】:

我经历了the official doc 和许多关于apiimplementation 配置之间区别的 *** 问题。我想我了解基本,但我真的很想了解依赖项暴露或不暴露是什么意思。

这是我到目前为止所得到的。当我发布我的Java库(用Kotlin编写,但不相关)时,发布的pom文件中的依赖范围是complie(使用api时)或runtime(使用implementation时),即

dependencies 
    api "..."

<dependency>
      <groupId>...</groupId>
      <artifactId>...</artifactId>
      <version>...</version>
      <scope>compile</scope> 
</dependency>
dependencies 
    implementation "..."

<dependency>
      <groupId>...</groupId>
      <artifactId>...</artifactId>
      <version>...</version>
      <scope>runtime</scope> 
</dependency>

那么在这种情况下暴露依赖真的只是意味着将它们添加到类路径(编译范围)吗?

One of the many answers about api vs implementation 说它只是关于构建优化,如果我们不在类路径中添加所有内容,构建时间会减少是有道理的吗?

还有一个额外的问题,Gradle 文档说 api 配置带有 java-library 插件,但显然,我可以在不应用插件的情况下使用它,这怎么可能?

// Gradle 6.1.1
plugins 
    id 'org.jetbrains.kotlin.jvm' version 'XXX'

dependencies 
    api "myLibrary"

【问题讨论】:

【参考方案1】:

那么在这种情况下暴露依赖真的只是意味着将它们添加到类路径(编译范围)吗?

是的,这几乎只是将它们放在消费者的编译类路径中与否的问题。

关于 api 与实现的众多答案之一说它仅仅是关于构建优化,如果我们不在类路径中添加所有内容可能会减少构建时间是有道理的吗?

嗯,好的软件设计提倡不暴露内部实现细节。这就是为什么您在代码中有公共和私有类成员的原因。您可能会争辩说,当涉及到依赖关系时,这个原则也是可靠的。我看到了以下好处:

消费者不会隐式开始依赖“内部”传递依赖。如果他们这样做了,则意味着您无法在不破坏消费者的情况下将它们从库中删除。 减少的类路径可能会使编译速度稍快一些。不过,我认为这对于普通项目来说并不重要。如果您依赖 Java 或 Kotlin 注释处理器或 Groovy AST 转换(感觉就像每次都扫描整个类路径),可能会更有影响力。 编译类路径中没有不必要的模块意味着如果这些模块发生更改,则不必重新编译库。

在我看来,最后一个是最大的好处。假设您有一个大型多项目,其中一个共享子项目在内部依赖于 Apache Commons Lang。如果您已将 Lang 声明为 api 依赖项并对其进行更新,则依赖此共享项目的所有其他项目都需要重新编译。如果您将其声明为实现依赖项,则不会发生这种情况。所有这些项目仍然需要重新测试原因,因为运行时行为可能已经改变(这在 Gradle 中默认正确处理)。

还有一个额外的问题,Gradle 文档说 api 配置带有 java-library 插件,但显然,我可以在不应用插件的情况下使用它,这怎么可能?

这是因为 Kotlin 插件还声明了一个 api 配置。它与 java-library 插件配置的语义相同。

如果您的项目是一个多项目,您仍然可以添加 java-library 插件,即使它使用的是 Kotlin 插件。这将导致的另一个变化是消费者将看到已编译类的输出目录,而不是最终的 jar 文件。这消除了在正常开发期间构建 jar 的需要,这将减少构建时间。另一方面,如果您在单个项目中有很多类,那么 Windows 上显然存在潜在的性能问题,因此通常的您的里程可能会有所不同免责声明也适用于此处(我不知道有多少“很多”)。

【讨论】:

以上是关于在 Gradle 中,api 配置暴露了依赖关系,而实现却没有,这到底意味着啥?的主要内容,如果未能解决你的问题,请参考以下文章

Android Gradle 依赖配置:implementation & api

Gradle:如何在java中以编程方式声明对项目特定配置的依赖关系

Java使用 Gradle 依赖配置compile,implementation和api的区别

Gradle + Buildship - 在 JAR 和项目之间切换依赖关系

Gradle 查看包的依赖关系

如何告诉 Grails 使用 Gradle 解决依赖关系