compile 配置(已过时)
- 传递性:
compile配置的依赖是传递性的,这意味着它们不仅对当前模块可见,而且对依赖当前模块的所有模块也可见。 - 编译和运行时可见:
compile配置的依赖在编译时和运行时都可见。
api 配置
- 传递性:
api配置的依赖也是传递性的,这意味着它们不仅对当前模块可见,而且对依赖当前模块的所有模块也可见。 - 编译和运行时可见:
api配置的依赖在编译时和运行时都可见。
选择 api 还是 compile
虽然 compile 配置已经过时,但理解它与 api 的区别有助于正确选择和使用 api 配置。实际上,api 配置是 compile 配置的直接替代品,提供了相同的功能,但在依赖管理和构建性能方面有更好的优化。
使用 api 的场景
- 公共 API:当依赖需要暴露给依赖当前模块的其他模块时,使用
api。例如,当前模块的公共 API 中使用了该依赖的类型。 - 库模块:如果你正在编写一个库模块,并且希望使用该库的其他模块能够访问某些依赖的类型和方法,那么使用
api。
使用 compile 的场景
- 过时配置:
compile配置已经过时,并在 Gradle 7.0 中被移除。新的项目和模块应避免使用compile配置,转而使用api或implementation配置。
implementation 配置
implementation 是 Gradle 新的依赖配置,用于声明编译时依赖。它在 Gradle 3.4 及之后的版本中引入,旨在替代 compile 配置。
特点
- 非传递性:
implementation配置的依赖是非传递性的,这意味着它们只对当前模块可见,而对依赖当前模块的其他模块不可见。这有助于减少模块之间的耦合,提高构建性能。 - 编译时可见:
implementation配置的依赖在编译时可见,但在运行时不可见。
选择 implementation 还是 api
- 使用
implementation:当依赖只在当前模块内部使用,不需要暴露给依赖当前模块的其他模块时,使用implementation。这有助于减少模块之间的耦合,提高构建性能。 - 使用
api:当依赖需要暴露给依赖当前模块的其他模块时,使用api。例如,当前模块的公共 API 中使用了该依赖的类型。
示例项目结构
假设有一个多模块项目,包含 app 模块和 library 模块:
library 模块的 build.gradle
dependencies {
implementation 'org.apache.commons:commons-lang3:3.12.0'
api 'com.google.guava:guava:30.1.1-jre'
}
app 模块的 build.gradle
dependencies {
implementation project(':library')
}
在这个示例中:
commons-lang3依赖只在library模块内部使用,不会暴露给app模块。guava依赖通过api配置暴露给app模块,因此app模块可以直接使用guava提供的类型和方法。
总结
compile:旧的依赖配置,已过时且在 Gradle 7.0 中被移除。依赖是传递性的,对编译时和运行时都可见。implementation:新的依赖配置,依赖是非传递性的,只对当前模块可见,对编译时可见但对运行时不可见。推荐在大多数情况下使用。api:新的依赖配置,依赖是传递性的,对编译时和运行时都可见。用于需要暴露给依赖当前模块的其他模块的依赖。
依赖树
在 Gradle 中,implementation 配置的依赖确实是非传递性的,这意味着它们不会自动传递给依赖当前模块的其他模块。然而,依赖树中仍然可能显示 implementation 配置的依赖,这是因为依赖树显示的是所有依赖的完整视图,包括直接依赖和传递依赖。
为了更好地理解这一点,我们可以通过一个具体的示例来说明。
示例项目结构
假设有一个多模块项目,包含 app 模块和 library 模块:
library 模块的 build.gradle
dependencies {
implementation 'org.apache.commons:commons-lang3:3.12.0'
api 'com.google.guava:guava:30.1.1-jre'
}
app 模块的 build.gradle
dependencies {
implementation project(':library')
}
依赖树
当你打印 app 模块的依赖树时,可能会看到类似以下的输出:
./gradlew :app:dependencies
implementation - Implementation only dependencies for source set 'main'. (n)
+--- project :library
| +--- com.google.guava:guava:30.1.1-jre
| | +--- ...
| --- org.apache.commons:commons-lang3:3.12.0
解释
-
implementation配置的依赖:library模块中使用implementation配置的commons-lang3依赖不会传递给app模块。这意味着app模块不能直接使用commons-lang3提供的类型和方法。- 但是,依赖树显示的是所有依赖的完整视图,包括直接依赖和传递依赖,因此你仍然会在依赖树中看到
commons-lang3。
-
api配置的依赖:library模块中使用api配置的guava依赖会传递给app模块。这意味着app模块可以直接使用guava提供的类型和方法。
验证传递性
为了验证 implementation 配置的非传递性,你可以尝试在 app 模块中使用 commons-lang3 提供的类型和方法。如果 app 模块无法编译通过,则说明 implementation 配置的依赖确实没有传递。
示例代码
在 app 模块中尝试使用 commons-lang3 提供的类型:
import org.apache.commons.lang3.StringUtils;
public class App {
public static void main(String[] args) {
// 尝试使用 commons-lang3 提供的 StringUtils
String reversed = StringUtils.reverse("Hello");
System.out.println(reversed);
}
}
如果 app 模块无法编译通过,并且出现类似以下的错误:
error: package org.apache.commons.lang3 does not exist
import org.apache.commons.lang3.StringUtils;
这说明 commons-lang3 依赖没有传递到 app 模块。
总结
- 依赖树显示:依赖树显示的是所有依赖的完整视图,包括直接依赖和传递依赖。因此,即使
implementation配置的依赖是非传递性的,你仍然会在依赖树中看到它们。 - 非传递性验证:通过尝试在依赖当前模块的其他模块中使用
implementation配置的依赖,可以验证它们是否真正传递。如果无法编译通过,则说明这些依赖没有传递。
依赖冲突
在 Gradle 中,依赖版本的解析遵循“最高版本优先”的原则。这意味着如果你的项目中有多个模块声明了相同的依赖库,但版本号不同,Gradle 会选择最高版本的依赖来解决冲突。
示例说明
假设你有一个多模块项目,包含 app 模块和 library 模块:
library 模块的 build.gradle
dependencies {
implementation 'org.apache.commons:commons-lang3:3.12.0'
}
app 模块的 build.gradle
dependencies {
implementation project(':library')
implementation 'org.apache.commons:commons-lang3:3.9.0'
}
在这个示例中,library 模块依赖 commons-lang3 版本 3.12.0,而 app 模块依赖 commons-lang3 版本 3.9.0。
依赖解析
当你构建项目时,Gradle 会解析依赖并选择最高版本的 commons-lang3,即 3.12.0。你可以通过打印依赖树来验证这一点:
./gradlew :app:dependencies
依赖树的输出可能类似于以下内容:
implementation - Implementation only dependencies for source set 'main'. (n)
+--- project :library
| --- org.apache.commons:commons-lang3:3.12.0
--- org.apache.commons:commons-lang3:3.9.0 -> 3.12.0
解释
library模块声明了commons-lang3版本3.12.0。app模块声明了commons-lang3版本3.9.0。- Gradle 解析依赖时,选择了最高版本
3.12.0,并将其应用于整个项目。
验证依赖版本
你可以通过以下代码验证实际使用的依赖版本:
import org.apache.commons.lang3.StringUtils;
public class App {
public static void main(String[] args) {
// 使用 commons-lang3 提供的 StringUtils
String reversed = StringUtils.reverse("Hello");
System.out.println(reversed);
// 打印 commons-lang3 版本
Package pkg = StringUtils.class.getPackage();
System.out.println("Commons Lang3 version: " + pkg.getImplementationVersion());
}
}
运行上述代码,你应该会看到输出的 commons-lang3 版本是 3.12.0。
总结
- 最高版本优先:Gradle 解析依赖时,会选择最高版本的依赖来解决冲突。
- 依赖树验证:通过打印依赖树,可以验证实际使用的依赖版本。
- 实际使用版本:在代码中可以通过反射或其他方式验证实际使用的依赖版本。