Gradle基础学习(五) 认识依赖管理

Gradle内置了对依赖管理的支持。

依赖管理是一种自动化的技术,用于声明和解决项目所需的外部资源。

在Gradle构建脚本中,我们定义了构建项目的过程,而这个过程可能需要外部依赖,可以是JAR文件、插件、库或源代码。

版本目录

版本目录提供了一种将依赖项声明集中到lib.versions.toml文件中的方法。

目录使得在子项目之间共享依赖项和版本配置变得简单,它还允许团队在大型项目中强制执行库和插件的版本。

版本目录通常包含四个部分:

1. versions - 申明要使用的插件和库的版本号

2. libraries - 定义构建文件中要使用的库

3. bundles -  定义一组依赖项

4. plugins - 定义插件

libs.versions.toml 文件的内容可能是这样的:

[versions]

androidGradlePlugin = "7.4.1"

mockito = "2.16.0"

[libraries]

googleMaterial = { group = "com.google.android.material", name = "material", version = "1.1.0-alpha05" }

mockitoCore = { module = "org.mockito:mockito-core", version.ref = "mockito" }

[plugins]

androidApplication = { id = "com.android.application", version.ref = "androidGradlePlugin" }

 在项目中,lib.versions.toml文件通常放在gradle目录下,应该加到版本控制里:

gradle/libs.versions.toml

如果在项目中没有找到这个文件,可能是因为项目还没有采用这种集中管理依赖版本的做法。

请注意,要使用版本目录功能,Gradle需要是 Gradle 6.0 及以上版本。

 

申明依赖

 要向项目添加依赖项,我们需要在build.gradle(.kts)文件的dependencies块中指定依赖项。

下面这个build.gradle(.kts)文件添加了一个插件和两个依赖项,这个项目使用了上面提到的版本目录:

①:对项目应用Android Gradle plugin, 这个插件增加了几个特定于构建Android Apps的功能。

②:向项目添加Material依赖项,Material Design提供了一些组件来创建Android用户界面。

③:向项目添加Mockito依赖项,Mockito是一个用于测试Java代码的模拟框架。这个库会被用于编译和运行此项目中的测试源代码。

上面的依赖项是按配置分组的。 

1.Material库被添加到了implementation配置中,用于编译和运行生产代码。

2.mockito-core库被添加到了testImplementation配置中,用于编译和运行测试代码。

 

查看项目依赖 

通过在命令行执行./gradlew :app:dependencies,可以查看项目的依赖关系树:

$ ./gradlew :app:dependencies

> Task :app:dependencies

------------------------------------------------------------

Project ':app'

------------------------------------------------------------

implementation - Implementation only dependencies for source set 'main'. (n)

\--- com.google.android.material:material:1.1.0-alpha05 (n)

testImplementation - Implementation only dependencies for source set 'test'. (n)

\--- org.mockito:mockito-core:2.16.0 (n)

...

依赖管理术语 

依赖管理中有着大量的术语,在这里我们会列举一些最常用的。

我们先来了解一下Gradle Component Model,方便理解后续提到的术语。

Gradle component model

Gradle 的依赖管理引擎是支持变体的(variant-aware)。这意味着 Gradle 不仅仅关注组件本身,还关注组件的不同使用方式,即组件的变体。这些变体代表了组件可以以不同的方式被使用,例如用于 Java 编译、原生链接或文档生成等。制品(Artifacts)是与这些变体相关联的,每个变体可以有一组不同的依赖。

当存在多个变体时,Gradle 如何决定选择哪一个呢?这是通过属性(attributes)来实现的。属性为变体提供了语义,并帮助依赖管理引擎产生一致的解析结果。当 Gradle 解析依赖时,它会根据请求的属性和提供的变体的属性来匹配最合适的变体。

Gradle 区分了两种类型的组件:

  1. 本地组件(如项目),这些组件是从源代码构建的。对于本地组件,变体被映射到可消费的配置(consumable configurations)。这意味着你可以定义不同的构建变体,例如不同的构建类型(如 debug 和 release)或不同的产品风味(product flavors),并且 Gradle 会根据这些变体来解析依赖。
  2. 外部组件,这些组件被发布到存储库中。对于外部组件,变体是由发布的 Gradle 模块元数据(Gradle Module Metadata)定义的,或者从 Ivy/Maven 元数据中派生出来的。Gradle 会从存储库中下载这些元数据,并根据请求的属性和元数据中定义的变体来解析最合适的依赖。

总结一下,Gradle 的依赖管理引擎通过支持组件的变体和使用属性来匹配最合适的变体,从而实现了灵活的依赖解析。这使得 Gradle 能够处理复杂的依赖关系,并在不同的构建环境和配置下提供一致的依赖解析结果。

Artifact

Artifact(制品)通常指的是由构建过程产生的文件或目录,这些文件或目录是为了供用户或其他项目使用,或者部署到托管系统上。

这些制品可以是任何形式,但最常见的是一些特定类型的文件,例如:

1.目录

比如一个项目里的源代码main目录,或者另一个项目的项目目录。

2.JARJava Archive)文件

在Java项目中,JAR文件是一种包含了编译后的Java类文件、相关的元数据和资源的归档文件。JAR文件通常用于分发Java库、应用程序模块或插件。

3.ZIP分发包

ZIP文件是一种常用的压缩格式,用于打包多个文件和目录。在构建过程中,ZIP文件可以被用来打包项目产生的所有文件,以便于分发或部署。

4.原生可执行文件

对于像C++或Go这样的语言,构建过程可能会产生可以直接在操作系统上运行的二进制文件,即原生可执行文件,这些文件通常是用户直接运行的程序。

制品通常是通过构建工具(如Gradle、Maven、Ant等)生成的,并且它们遵循一定的命名和版本控制约定,以便于依赖管理和版本追踪。

在构建过程中,制品可能是单个文件(如JAR或ZIP文件),也可能是包含多个文件和子目录的目录结构。当制品被设计为供其他项目使用或部署到托管系统时,它们通常是以单个文件的形式存在。然而,在跨项目的依赖关系中,使用目录作为制品更为常见,这是因为它们可以避免产生可发布的制品所带来的成本。

例如,在一个大型的软件项目中,不同的模块或组件可能会有相互依赖的关系。在这种情况下,一个模块可能会直接引用另一个模块的目录结构,而不是通过发布和引入一个单独的JAR或ZIP文件。这样做可以减少构建和部署过程中的复杂性,并允许更灵活的项目结构和依赖管理。

 

Component

Component(组件)是指一个模块的任意单一版本。对于外部库来说,组件指的是该库的一个已发布的版本。

在构建过程中,组件是由插件(例如Java库插件)定义的,并提供了一种简单的方式来定义用于发布的发布物(publication)。

组件包括制品(artifacts)以及描述组件变体(variants)的适当元数据。例如,在默认设置下,Java组件由一个JAR文件组成,该文件是由jar任务生成的,并且包含了Java api和运行时变体的依赖信息。它还可以定义额外的变体,如源代码和Javadoc,以及相应的制品。

Configuration

Configuration (配置)是一组为特定目标组合在一起的命名依赖项集合。配置提供了对底层已解析模块及其制品的访问。

通过配置,你可以指定哪些依赖项应该在特定的构建阶段(如编译、测试或运行时)中使用。

Gradle 提供了几个预定义的配置,如 implementation、api、testImplementation、runtimeOnly 等,每个配置都有其特定的用途和行为。例如,implementation 配置用于实现项目的依赖,而 testImplementation 配置用于测试代码的依赖。

Dependency

依赖是指构建项目所需的外部资源,它是一个项目对其他项目的依赖关系,Gradle能够自动下载、配置和管理这些依赖项。

Gradle支持多种类型的依赖项:

1.外部库依赖 

这些依赖项通常来自远程仓库,如Maven Central。开发者可以通过指定依赖项的坐标(group、name和version)来声明这些依赖项。例如:

implementation 'com.example:library:1.0.0'

2.项目依赖

在一个多模块项目中,一个模块可能依赖于另一个模块。这种依赖关系可以通过使用project关键字来声明。例如:

implementation project(':subproject')

3.本地JAR依赖

如果依赖项是一个本地JAR文件,Gradle也支持通过files或fileTree方法来声明这些依赖项。例如:

implementation files('libs/local-library.jar')

implementation fileTree(dir: 'libs', include: ['*.jar'])

在使用依赖的时候存在直接依赖和传递依赖: 

1.直接依赖

直接依赖是指组件直接需要的依赖。直接依赖也被称为第一级依赖。例如,如果您的项目源代码需要Guava,则应该将Guava声明为直接依赖项。

2.传递依赖

传递依赖是指组件需要这个依赖,仅仅是因为另一个依赖需要它。

依赖管理问题通常与传递依赖有关。开发人员经常通过添加直接依赖来错误地修复传递依赖问题。为了避免这种情况,Gradle提供了依赖约束的概念。

Dependency Constraint 

Dependency Constraint(依赖约束)是一种规则,它定义了模块需要满足的条件,以确保它们能够作为依赖项被正确地解析和使用。这些条件可以涉及模块的版本、兼容性或其他相关因素。通过设置依赖约束,我们可以缩小支持的模块版本范围,从而确保项目的依赖关系稳定和可靠。这对于管理复杂的项目依赖关系非常重要,可以避免版本冲突和其他潜在问题。

当Gradle尝试解析依赖项到模块版本时,会考虑该模块的所有带版本号的依赖声明、所有传递依赖以及所有依赖约束。

Gradle会选择匹配所有条件的最高版本。如果没有找到这样的版本,Gradle就会失败,并显示一个冲突声明的错误。此时你可以调整依赖或依赖约束申明,或者根据需要对传递依赖项进行其他调整。和依赖申明类似,依赖约束申明也具有作用域,因此可以为构建的部分选择性地定义它们。

让我们通过一个示例来进一步说明依赖约束如何工作:

//build.gradle.kts

dependencies {

    implementation("org.apache.httpcomponents:httpclient")

    constraints {

        implementation("org.apache.httpcomponents:httpclient:4.5.3") {

            because("previous versions have a bug impacting this application")

        }

        implementation("commons-codec:commons-codec:1.11") {

            because("version 1.9 pulled from httpclient has bugs affecting this application")

        }

    }

}

首先,implementation("org.apache.httpcomponents:httpclient")声明了项目对httpclient库的依赖,但省略了版本信息。这意味着Gradle将使用它认为合适的最新版本,除非有约束条件指定了不同的版本。

在约束块中,我们定义了需要的依赖的版本,其中,只有当commons-codec作为传递性依赖被引入时,commons-codec:1.11的版本定义才会被考虑,因为commons-codec没有被定义为项目的直接依赖。如果commons-codec没有被作为传递性依赖引入,那么这个约束就没有效果。

依赖约束也可以定义更丰富的版本约束,并支持严格版本约束,即使这与传递依赖定义的版本相矛盾(例如,如果版本需要降级)。这意味着,即使一个库的传递依赖需要一个不同的版本,你也可以使用依赖约束来强制使用某个特定版本。

当使用Gradle来发布和消费模块时,依赖约束可以得到完整的支持。但是,如果你使用Maven或Ivy来消费这些模块,那么Gradle生成的模块元数据(包括依赖约束)将不会被这些工具所理解。这些约束确保了即使依赖项是由其他库作为传递依赖引入的,Gradle也会尽量使用满足约束条件的版本。

Feature Variant

Feature Variant(功能变体)通常指的是组件的一个特性,这个特性可以被单独选择或不被选择。功能变体通过一种或多种能力(Capabilities)来标识。

能力标识了一个或多个组件提供的特性,并通过类似于模块版本使用的坐标来标识。

功能变体允许开发者根据特定需求或目标来组合不同的功能,以满足不同用户或场景的需求。

Module 

Module(模块)是指随时间演变的软件组件或代码库,例如 Google Guava。每个模块都有一个唯一的名称,以便在依赖管理和版本控制中进行标识。

模块的每一个Release版都会使用一个Module version (模块版本) 来表示,为了方便使用它们,通常会被托管在代码仓库(Repository)中。

Module version

Module Version(模块版本)指的是已发布模块的特定变更集合。例如18.0表示坐标为com.google:guava:18.0的模块版本。

通过指定版本号,可以确保开发者、用户和系统知道他们正在使用哪个版本的模块,以及该版本包含了哪些更改。

在实践中,对模块版本方案并没有严格的限制。开发者可以根据自己的需要选择适合的版本号格式。然而,为了确保版本控制的透明性和可预测性,许多项目和社区都遵循某种标准的版本编号策略。目前最广泛使用的版本编号策略是语义版本控制(Semantic Versioning),也被称为“SemVer”。SemVer 是一种用于软件项目版本控制的规范,它提供了一套明确的规则,用于指定版本号以及如何解读这些版本号。根据 SemVer,版本号通常由三个部分组成:主版本号(Major)、次版本号(Minor)和修订号(Patch)。例如,版本号 1.2.3 表示主版本为 1,次版本为 2,修订号为 3。

Platform

Platform(平台)通常指的是一组要一起使用的模块或组件。这些平台可以根据不同的用例被分类为不同的类别,包括:

1. 模块集(Module Set):这通常指的是作为整体一起发布的模块集合。使用集合中的一个模块通常意味着我们也希望对集合中的所有模块使用相同的版本。例如,如果使用 groovy 1.2 版本,也使用 groovy-json 1.2 版本。

2. 运行环境(Runtime Environment):这是一组已知能够良好协作的库。例如,Spring Platform 就是一种运行环境,它为 Spring 和与 Spring 配合良好的组件提供了推荐的版本。

3. 部署环境(Deployment Environment):这指的是用于运行应用程序的环境,如 Java 运行时环境(JRE)、应用服务器等。

这里是一个使用platform的例子:

//build.gradle.kts

dependencies {

    // import a BOM

    implementation(platform("org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE"))

    // define dependencies without versions

    implementation("com.google.code.gson:gson")

    implementation("dom4j:dom4j")

}

在本例中,gson和dom4j的版本由平台Spring Boot BOM提供。 

  • 27
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值