我试图弄清楚在构建依赖关系时api和实现配置之间的区别。

在文档中,它说实现有更好的构建时间,但是,在一个类似的问题中看到这个评论,我想知道这是否是真的。

因为我不是Gradle的专家,我希望有人能帮助我。我已经阅读了文档,但我想知道一个简单易懂的解释。


当前回答

假设你有一个应用模块,它使用lib1作为库,lib1使用lib2作为库。就像这样:app -> lib1 -> lib2。

现在当在lib1中使用api lib2时,当在app模块中使用api lib1或实现lib1时,app可以看到lib2代码。

但是当在lib1中使用实现lib2时,应用程序无法看到lib2代码。

其他回答

来自gradle文档:

让我们看一下基于jvm的项目的一个非常简单的构建脚本。

plugins {
    id 'java-library'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.hibernate:hibernate-core:3.6.7.Final'
    api 'com.google.guava:guava:23.0'
    testImplementation 'junit:junit:4.+'
}

实现 编译项目的生产源代码所需的依赖项,这些依赖项不是项目公开的API的一部分。例如,该项目使用Hibernate进行内部持久层实现。 api 编译项目生产源代码所需的依赖项,这些依赖项是项目公开的API的一部分。例如,该项目使用了Guava,并在方法签名中公开了带有Guava类的公共接口。

我喜欢把api依赖看作是公共的(其他模块可以看到),而实现依赖则是私有的(只有这个模块可以看到)。

注意,与公共/私有变量和方法不同,api/实现依赖关系不是由运行时强制执行的。这只是一个构建时优化,它允许Gradle知道当某个依赖项更改API时需要重新编译哪些模块。

假设你有一个应用模块,它使用lib1作为库,lib1使用lib2作为库。就像这样:app -> lib1 -> lib2。

现在当在lib1中使用api lib2时,当在app模块中使用api lib1或实现lib1时,app可以看到lib2代码。

但是当在lib1中使用实现lib2时,应用程序无法看到lib2代码。

@matpag和@dev-bmax的回答足够清楚,让人们理解实现和api之间的不同用法。我只是想从另一个角度做一个额外的解释,希望对有同样问题的人有所帮助。

我创建了两个项目进行测试:

项目A作为一个名为“frameworks-web-gradle-plugin”的java库项目依赖于“org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE” 项目B依赖于项目A的实现'com.example.frameworks.gradle:frameworks-web-gradle-plugin:0.0.1-SNAPSHOT'

上面描述的依赖层次结构如下:

[project-b] -> [project-a] -> [spring-boot-gradle-plugin]

然后我测试了以下场景:

Make project A depends on 'org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE' by implementation . Run gradle dependencies command in a terminal in poject B root dir,with following screenshot of output we can see that 'spring-boot-gradle-plugin' appears in runtimeClasspath dependencies tree, but not in compileClasspath's, I think that's exactly why we can't make use of library that declared using implementation, it just won't through compilation. Make project A depends on 'org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE' by api Run gradle dependencies command in a terminal in poject B root dir again. Now 'spring-boot-gradle-plugin' appears both in compileClasspath and runtimeClasspath dependencies tree.

我注意到一个显著的区别是,在生产者/库项目中以实现方式声明的依赖项不会出现在消费者项目的compileClasspath中,因此我们不能在消费者项目中使用相应的库。

关于api和实现还有一个技术注意事项。假设你有以下依赖项:

dependencies {
  api "com.example:foo:1.0"
  implementation "com.example:bar:1.0"
}

如果你在本地Maven存储库中安装了一个生成的jar文件(借助Maven -publish插件),你会看到生成的pom.xml文件如下所示:

    <dependency>
      <groupId>com.example</groupId>
      <artifactId>foo</artifactId>
      <version>1.0</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>com.example</groupId>
      <artifactId>bar</artifactId>
      <version>1.0</version>
      <scope>runtime</scope>
    </dependency>

注意:api被转换为编译作用域和实现到运行时作用域。

这允许这个库的使用者避免在他们的编译类路径中有运行时依赖项。