vlambda博客
学习文章列表

读书笔记《gradle-effective-implementations-guide-second-edition》依赖关系管理

Chapter 5.  Dependency Management

当我们开发代码时,我们通常使用第三方或开源库。这些库需要在编译器的类路径中可用,否则我们将得到错误并且我们的构建将失败。 Gradle 提供了对依赖管理的支持,所以我们可以在构建文件中定义我们的依赖。然后,Gradle 将为我们的各种任务处理必要的配置。

在本章中,我们将讨论如何在构建中使用依赖管理。我们将看到如何使用配置来组织依赖关系。我们还将讨论托管依赖项工件的存储库、它们的依赖项以及如何处理不同的存储库布局。

然后,我们将使用 Gradle 语法为具有版本信息的模块定义依赖关系。

Dependency configuration


Java 没有真正支持将版本化库作为依赖项使用。例如,我们无法用 Java 表示我们的类是依赖于 lib-1.0.jar 还是lib-2.0.jar。有一些开源解决方案可以处理依赖关系,并允许我们表达我们的 Java 代码是依赖于 lib-1.0.jar 还是 lib -2.0.jar。最受欢迎的是 Maven 和 Apache Ivy。 Maven 是一个完整的构建工具,并具有依赖管理机制。 Ivy 只涉及依赖管理。

这两种工具都支持版本库与有关这些库的元数据一起存储的存储库。一个库可以依赖于其他库,并在该库的元数据中进行描述。元数据在描述符 XML 文件中描述。 Ivy 完全支持 Maven 描述符文件和存储库;它还增加了一些额外的功能。因此,使用 Ivy,您将获得与 Maven 相同的功能,然后获得更多功能。这就是 Gradle 在底层使用 Ivy API 来执行依赖管理的原因。 Gradle 还在 Ivy 之上添加了一些额外的糖,因此我们可以以非常灵活的方式定义和使用依赖项。

在 Gradle 构建文件中,我们将依赖项组合在一个配置中。配置具有名称,并且配置可以相互扩展。通过配置,我们可以创建逻辑依赖组。例如,我们可以创建一个 javaCompile 配置来包含编译 Java 代码所需的依赖项。我们可以根据需要向构建中添加任意数量的配置。我们没有直接在配置中定义我们的依赖关系。当我们定义依赖时,可以使用配置,就像标签一样。

每个 Gradle 构建都有一个 ConfigurationContainer 对象。此对象可通过名称为 containers 的 Project 属性访问。我们可以使用闭包来配置带有 Configuration 对象的容器。每个 Configuration 对象至少有一个名称,但我们可以更改更多属性。如果配置与依赖项有版本冲突,我们可以设置解决策略,或者我们可以更改配置的可见性,使其在项目之外不可见。  Configuration 对象也有一个层次结构。所以我们可以从现有的 Configuration 对象扩展继承设置。

在下面的示例中,我们将创建一个名为 commonsLib 的新配置来保存我们的依赖项和一个扩展的mainLib 配置  ;commonsLib。扩展的 mainLib配置从 commonsLib获取所有设置和依赖,我们也可以分配额外的依赖:

configurations { 
    commonsLib { 
        description = 'Common libraries' 
    } 
    mainLib { 
        extendsFrom commonsLib 
        description = 'Main libraries' 
    } 
} 
 
// Reference mainLib configuration 
// using [] syntax for the 
// configuration container. 
println configurations['mainLib'].name 
 
// Reference commonsLib in another way, 
// just use the name directly as property. 
println configurations.commonsLib.name 

构建的输出显示了配置的名称,如下所示:

$ gradle -q
mainLib
commonsLib
Welcome to Gradle 2.9.
To run a build, run gradle <task> ...
To see a list of available tasks, run gradle tasks
To see a list of command-line options, run gradle --help
To see more detail about a task, run gradle help --task <task>

许多插件向 ConfigurationContainer 添加了新的配置。我们在上一章中使用了 Java 插件,它为我们的项目添加了四个配置。使用内置 dependencies 任务,我们可以大致了解项目的已定义依赖项和配置。

以下构建脚本使用 Java 插件:

apply plugin: 'java' 

如果我们执行 dependencies 任务,我们会得到以下输出:

$ gradle -q dependencies
------------------------------------------------------------
Root project
------------------------------------------------------------
archives - Configuration for archive artifacts.
No dependencies
compile - Compile classpath for source set 'main'.
No dependencies
default - Configuration for default artifacts.
No dependencies
runtime - Runtime classpath for source set 'main'.
No dependencies
testCompile - Compile classpath for source set 'test'.
No dependencies
testRuntime - Runtime classpath for source set 'test'.
No dependencies

注意我们的项目中已经有六个配置。下表显示了配置和使用该配置的任务:

配置

扩展

由任务使用

说明

编译

-

编译Java

这些是编译时编译源文件所需的依赖项

运行时

编译

-

这些是应用程序运行时的依赖项,但编译不需要

testCompile

编译

compileTestJava

这些是编译测试源文件的依赖项

testRuntime

testCompile

测试

这些是运行测试所需的所有依赖项

档案

-

上传档案

这包含工件,例如项目创建的 JAR 文件

默认

运行时

-

这是包含所有运行时依赖项的默认配置

如果我们的代码对库有依赖,我们可以使用 compile 配置来设置依赖。然后,依赖项在 runtime、 testCompile、 testRuntime 和 默认 配置。

Repositories


依赖项通常存储在某种存储库中。存储库具有定义版本库模块路径模式的布局。例如,Gradle 知道 Maven 存储库的布局。 Ivy 存储库可以有自定义布局,使用 Gradle,我们可以配置自定义布局。可以通过文件系统、HTTP、SSH 或其他协议访问存储库。

我们可以在 Gradle 构建文件中声明几种存储库类型。 Gradle 提供了一些预配置的存储库,但使用自定义 Maven 或 Ivy 存储库也非常容易。我们还可以声明一个简单的文件系统存储库,用于解析和查找依赖项。下表显示了我们可以使用的预配置和自定义存储库:

存储库类型

说明

Maven 仓库

这是远程计算机或文件系统上的 Maven 布局存储库。

Bintray JCenter 存储库

这是预配置的 Maven 布局存储库,用于在 Bintray JCenter 存储库中搜索依赖项。这是 Maven 中央存储库的超集。

Maven 中央存储库

这是预配置的 Maven 布局存储库,用于在 Maven 中央存储库中搜索依赖项。

Maven本地仓库

这是在本地 Maven 存储库中查找依赖项的预配置 Maven 存储库。

常春藤仓库

这是可以位于本地或远程计算机上的 Ivy 存储库。

平面目录存储库

这是计算机本地文件系统或网络共享上的简单存储库。

我们使用 repositories() 方法定义一个存储库。此方法接受用于配置 org.gradle.api.artifacts.dsl.RepositoryHandler 对象的闭包。

Adding Maven repositories

许多 Java 项目使用 Maven 作为构建工具,并使用它的依赖项管理功能。 Maven 存储库存储库,其中包含在描述符 XML 文件中描述的版本信息和元数据。 Maven 存储库的布局是固定的,并遵循 someroot/[organization]/[module]/[revision]/ [module]-[revision].[ext] 模式。  organization 部分根据组织名称中使用的点拆分为子文件夹。例如,如果组织名称是 org.gradle,则 org文件夹与 gradle 子文件夹需要在 Maven 存储库中。一个带有组织名称的 JAR 库, org.gradle;模块名称, gradle-api;和修订版, 1.0,通过 someroot/org/gradle/gradle-api/1.0/gradle-api-1.0解决。 jar 路径。

Bintray JCenter 是一个相对较新的公共 Maven 存储库,其中存储了很多 Maven 开源依赖项。它是 Maven 中央存储库的超集,还包含直接在 JCenter 发布的依赖项工件。此外,将我们自己的工件部署到 Bintray JCenter 非常容易。访问存储库的 URL 是 https://jcenter.bintray.com。 Gradle 为 JCenter 提供了一个快捷方式,这样我们就不必在 repositories 配置块中自己输入 URL。快捷方式是 jcenter()

Maven 中央存储库位于 https://repo1.maven.org/maven2并包含很多库。许多开源项目将其工件部署到 Maven 的中央存储库。我们可以在 repositories()方法的配置闭包中使用 mavenCentral()方法。以下示例是一个构建文件,我们在其中定义了 Maven 中央存储库和 Bintray JCenter:

repositories { 
    // Define Bintray's JCenter 
    // repository, to find 
    // dependencies. 
    jcenter() 
 
    // Define Maven Central 
    // as repository for 
    // dependencies. 
    mavenCentral() 
} 

如果您以前在计算机上使用过 Maven,那么您很有可能拥有一个本地 Maven 存储库。 Maven 将使用我们主目录中的隐藏文件夹来存储下载的依赖库。我们可以使用 mavenLocal() 方法将这个本地 Maven 存储库添加到存储库列表中。我们可以将 Maven 本地存储库添加到我们的构建文件中,如下所示:

repositories { 
    mavenLocal() 
} 

Bintray JCenter 和 Maven 中央和本地存储库是预配置的 Maven 存储库。我们还可以添加一个遵循 Maven 布局的自定义存储库。例如,我们公司可以通过 Intranet 提供一个 Maven 存储库。我们使用 maven()mavenRepo() 方法定义 Maven 存储库的 URL。

示例构建文件使用这两种方法添加两个新的 Maven 存储库,可通过我们的内网获得,如下所示:

repositories { 
    maven { 
        // Name is optional. If not set url property is used 
        name = 'Main Maven repository' 
        url = 'http://intranet/repo' 
    } 
 
    // Alternative way for defining a custom 
    // Maven repository. 
    mavenRepo( 
        name: 'Snapshot repository', 
        url: 'http://intranet/snapshots') 
} 

这两种方法都通过闭包和方法参数的组合来配置存储库。有时我们必须访问将元数据存储在描述符 XML 文件中的 Maven 存储库,但实际的 JAR 文件位于不同的位置。为了支持这种情况,我们必须设置 artifactUrls 属性并分配存储 JAR 文件的服务器的地址:

repositories { 
    maven { 
        url: 'http://intranet/mvn' 
 
        // Use a different location for 
        // the artifacts. 
        artifactUrls 'http://intranet/jars' 
        artifactUrls 'http://intranet/snapshot-jars' 
    } 
} 

要使用基本身份验证访问 Maven 存储库,我们可以在定义存储库时设置凭据,如下所示:

repositories { 
    maven(name: 'Secured repository') { 
 
        // Set credentials to access 
        // the repository. It is better 
        // to store the values for username 
        // and password outside the build file. 
        credentials { 
            username = 'username' 
            password = 'password' 
      } 
 
      url = 'http://intranet/repo' 
    } 
} 

username 和 password 作为纯文本存储在构建文件中并不是一个好主意,因为任何人都可以读取我们的密码,如果以纯文本形式存储。如果我们在 Gradle 用户主目录的gradle.properties 文件中定义属性,对属性文件应用正确的安全约束,并在我们的构建文件。

Adding Ivy repositories

常春藤存储库具有可自定义的布局,这意味着没有像 Maven 存储库那样的单一预定义布局。 Ivy 存储库的默认布局具有 someroot/[organization]/[module]/[revision]/[type]s/[artifact].[ext] 模式。与 Maven 布局一样,组织的名称不会拆分为子文件夹。因此,我们的 gradle 模块具有 org.gradle 组织名称和 gradle- api 带有修订版的工件 1.0 通过 someroot/org.gradle/gradle/1.0/jars/gradle-api 解决.jar 路径。

我们使用相同的 repositories() 方法来配置 Ivy 存储库。我们使用 ivy() 方法来配置 Ivy 存储库的设置。我们定义存储库的 URL,以及可选的名称,如下所示:

repositories { 
    ivy( 
        url: 'http://intranet/ivy-repo', 
        name: 'Our repository') 
 
    // Alternative way of defining 
    // an Ivy repository. 
    ivy { 
        url = 'http://intranet/ivy-snapshots' 
    } 
} 

如果我们的 Ivy 存储库有 Maven 布局,我们可以将 layout 属性设置为 maven。我们可以使用相同的属性为存储库定义自定义布局。我们将定义用于解析描述符 XML 文件和实际库文件的模式。

下表显示了我们可以使用的不同布局名称以及预配置布局的默认模式:

布局名称

模式 Ivy 描述符

模式工件

gradle

someroot/[组织]/[模块]/

[revision]/ivy-[revision].xml

someroot/[organization]/[module]/[revision]/

[artifact]-[revision](-[classifier])(.[ext])

模式  

someroot/[组织]/[模块]/

[revision]/ivy-[revision].xml

someroot/[organization]/[module]/[revision]/

[artifact]-[revision](-[classifier])(.[ext])

gradle 

风俗

定制         

示例构建文件使用预配置的布局名称 gradle 和 maven 以及自定义模式,如下所示:

repositories { 
    ivy { 
        url = 'http://intranet/ivy-snapshots' 
        // Repository follows the maven layout. 
        layout = 'maven' 
    } 
    ivy { 
        url = 'http://intranet/repository' 
        // Repository follows the gradle layout. 
        layout = 'gradle' 
    } 
 
    ivy { 
        url = 'http://intranet/custom' 
        layout('pattern') { 
            // Pattern to resolve Ivy descriptor files. 
            ivy '[module]/[revision]/ivy.xml' 
            // Pattern to resolve files. 
            artifact '[module]/[revision]/[artifact](.[ext])' 
      } 
    } 
} 

除了使用 layout() 方法来定义自定义模式,我们可以使用 ivyPattern() 和 artifactPattern() 定义 Ivy 存储库模式的方法:

repositories { 
    ivy { 
        url = 'http://intranet/custom' 
        ivyPatterns '[module]/[revision]/ivy.xml' 
        artifactPatterns '[module]/[revision]/[artifact](.[ext])' 
    } 
} 

要访问使用基本身份验证保护的 Ivy 存储库,我们必须传递我们的凭据。就像安全的 Maven 存储库一样,最好将用户名和密码作为属性存储在 $USER_HOME/.gradle/gradle.properties 文件中:

repositories { 
    ivy { 
        credentials { 
            // Username and password are from 
            // external gradle.properties file. 
            username = usernameFromGradleProperties 
            password = passwordFromGradleProperties 
      } 
      url = 'http://intranet/custom' 
      ivyPatterns '[module]/[revision]/ivy.xml' 
      artifactPatterns '[module]/[revision]/[artifact](.[ext])' 
      artifactPatterns '[module]/[revision]/[artifact](.[ext])' 
    } 
} 

Adding a local directory repository

要使用本地文件系统上的简单存储库或映射为本地存储的网络共享,我们必须使用 flatDir() 方法。  flatDir() 方法接受参数或闭包来配置正确的目录。我们可以分配单个目录或多个目录。

Gradle 将使用它找到的第一个匹配来解析配置目录中的文件,并使用以下模式:

  • [artifact]-[version].[ext]

  • [artifact]-[version]-[classifier].[ext]

  • [工件].[ext]

  • [artifact]-[classifier].[ext]

以下示例构建文件定义了一个平面目录存储库:

repositories { 
    flatDir( 
        dir: '../lib', 
        name: 'libs directory') 
 
    // Alternative way to define 
    // flat directory as repository. 
    flatDir { 
        dirs '../project-files', '/volumes/shared-libs' 
        name = 'All dependency directories' 
    } 
} 

Defining dependencies


我们讨论了如何使用依赖配置将依赖组合在一起;我们还看到了必须如何定义存储库以便解决依赖关系,但我们还没有讨论如何定义实际的依赖关系。我们在构建项目中使用 dependencies{} 脚本块定义依赖项。我们定义了一个闭包以传递给 dependencies{} 脚本块,其中包含依赖项的配置。

我们可以定义不同类型的依赖。下表显示了我们可以使用的类型:

依赖类型

方法

说明

外部模块依赖

-

这是对存储库中外部模块或库的依赖。

项目依赖

project()

这是对另一个 Gradle 项目的依赖。

文件依赖

files()fileTree()

这是对本地计算机上文件集合的依赖。

客户端模块依赖

module()

这是对外部模块的依赖,其中工件存储在存储库中,但有关模块的元信息在构建文件中。我们可以使用这种类型的依赖覆盖元信息。

Gradle API 依赖

gradleApi()

这是对当前 Gradle 版本的 Gradle API 的依赖。我们在开发 Gradle 插件和任务时使用此依赖项。

本地 Groovy 依赖项

localGroovy()

这是对当前 Gradle 版本使用的 Groovy 库的依赖。我们在开发 Gradle 插件和任务时使用此依赖项。

Using external module dependencies

最常用的依赖是外部模块依赖。我们可以用不同的方式定义一个模块依赖。例如,我们可以使用参数来设置组名、模块名和依赖的修订版。我们还可以使用 String 表示法在单个字符串中设置组名、模块名和修订版。我们总是将依赖项分配给特定的依赖项配置。依赖配置必须由我们自己定义或者我们已经应用到我们的项目的插件。

在下面的示例构建文件中,我们将使用 Java 插件,以便获得 compileruntime 依赖配置。我们还将使用不同的语法规则为每个配置分配几个外部模块依赖项:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    // Use attributes for the group, name and 
    // version for the external module dependency. 
    compile(group: 'org.springframework', 
      name: 'spring-core', 
      version: '4.2.3.RELEASE') 
 
    // Use String notation with group, name and 
    // version in a single String. 
    runtime('org.springframework:spring-aop:4.2.3.RELEASE' 
} 

请记住,Gradle 构建文件是一个 Groovy 脚本文件,因此我们可以定义变量来设置值并在 dependencies{} 脚本块配置闭包中使用它们。如果我们重写之前的构建文件,我们会得到以下输出:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
ext { 
    springVersion = '4.2.3.RELEASE' 
    springGroup = 'org.springframework' 
} 
 
dependencies { 
    // Use attributes to define dependency and 
    // refer to project properties. 
    compile(group: springGroup, 
      name: 'spring-core', 
      version: springVersion) 
 
    // Use String notation with expression support 
    // for variables. 
    runtime("$springGroup:spring-aop:$springVersion") 
} 

Gradle 将在 JCenter 存储库中查找描述符文件。如果找到该文件,则下载模块的工件和模块的依赖项,并使其可用于依赖项配置。

要查看依赖项和传递依赖项,我们调用内置的 dependencies 任务。我们得到以下输出:

$ gradle -q dependencies
------------------------------------------------------------
Root project
------------------------------------------------------------
archives - Configuration for archive artifacts.
No dependencies
compile - Compile classpath for source set 'main'.
\--- org.springframework:spring-core:4.2.3.RELEASE
    \--- commons-logging:commons-logging:1.2
default - Configuration for default artifacts.
+--- org.springframework:spring-core:4.2.3.RELEASE
|    \--- commons-logging:commons-logging:1.2
\--- org.springframework:spring-aop:4.2.3.RELEASE
    +--- aopalliance:aopalliance:1.0
    +--- org.springframework:spring-beans:4.2.3.RELEASE
    |    \--- org.springframework:spring-core:4.2.3.RELEASE (*)
    \--- org.springframework:spring-core:4.2.3.RELEASE (*)
runtime - Runtime classpath for source set 'main'.
+--- org.springframework:spring-core:4.2.3.RELEASE
|    \--- commons-logging:commons-logging:1.2
\--- org.springframework:spring-aop:4.2.3.RELEASE
    +--- aopalliance:aopalliance:1.0
    +--- org.springframework:spring-beans:4.2.3.RELEASE
    |    \--- org.springframework:spring-core:4.2.3.RELEASE (*)
    \--- org.springframework:spring-core:4.2.3.RELEASE (*)
testCompile - Compile classpath for source set 'test'.
\--- org.springframework:spring-core:4.2.3.RELEASE
    \--- commons-logging:commons-logging:1.2
testRuntime - Runtime classpath for source set 'test'.
+--- org.springframework:spring-core:4.2.3.RELEASE
|    \--- commons-logging:commons-logging:1.2
\--- org.springframework:spring-aop:4.2.3.RELEASE
    +--- aopalliance:aopalliance:1.0
    +--- org.springframework:spring-beans:4.2.3.RELEASE
    |    \--- org.springframework:spring-core:4.2.3.RELEASE (*)
    \--- org.springframework:spring-core:4.2.3.RELEASE (*)
(*) - dependencies omitted (listed previously)

要仅下载外部依赖项的工件而不下载传递依赖项,我们可以将依赖项的 transitive 属性设置为 false。我们可以使用闭包或作为参数列表中的额外属性来设置属性,如下所示:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    // Configure transitive property with closure. 
    compile('org.slf4j:slf4j-simple:1.7.13') { 
        transitive = false 
    } 
 
    // Or we can use the transitive property 
    // as argument. 
    compile(group: 'org.slf4j', 
      name: 'slf4j-simple', 
      version: '1.7.13', 
      transitive: false) 
} 

我们还可以使用 exclude() 方法排除一些传递依赖。 Gradle 将查看模块的描述符文件并排除我们使用 exclude() 方法添加的任何依赖项。

例如,在下面的构建文件中,我们排除了 org.slf4j:sl4j-api 传递依赖:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    // Configure transitive property with closure. 
    compile('org.slf4j:slf4j-simple:1.7.13') { 
        exclude 'org.slf4j:slf4j-api' 
    } 
} 

要获得仅具有外部模块依赖项的工件,我们可以使用 artifact-only 表示法。当存储库没有模块描述符文件并且我们想要获取工件时,我们也必须使用此表示法。我们必须在工件的扩展之前添加一个 @ 符号。当我们使用这种表示法时,Gradle 不会查看模块描述符文件(如果可用):

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    // Use artifact-only notation with @ symbol. 
    runtime('org.slf4j:slf4j-simple:1.7.13@jar') 
 
    // Or we can use the ext property 
    // as method argument. 
    runtime(group: 'org.slf4j', 
      name: 'slf4j-simple', 
      version: '1.7.13', 
      ext: 'jar') 
} 

我们甚至可以在完整配置上设置传递行为。每个配置都有一个 transitive 属性。为了改变我们在配置。在以下示例构建文件中,我们在 runtime 配置上设置了 transitive 属性:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    compile('org.slf4j:slf4j-simple:1.7.13') 
} 
 
// Make all dependencies for the compile 
// configuration transitive. 
configurations.compile.transitive = false 

在 Maven 存储库中,我们可以对依赖项使用分类器。例如,模块描述符文件为库的不同 JDK 版本定义了 jdk16jdk15 分类器。我们可以在 Gradle 依赖定义中使用 classifier 来选择具有给定分类器的依赖,如下所示:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    // Use artifact-only notation with @ symbol 
    // together with classifier jdk16. 
    compile('sample:simple:1.0:jdk16@jar') 
 
    // Or we can use the classifier property 
    // as method argument. 
    compile( 
      group: 'sample', 
      name: 'simple', 
      version: '1.0', 
      classifier: 'jdk16') 
} 

一个 Maven 仓库中一个模块的模块描述符只能有一个工件;但是在 Ivy 存储库中,我们可以为单个模块定义多个工件。每组工件都在一个配置中组合在一起。默认配置包含属于该模块的所有工件。如果我们在为 Ivy 模块定义依赖项时没有指定配置属性,则使用 default 配置。如果我们想使用属于这个特定配置的工件,我们必须指定 configuration 属性,如下所示:

apply plugin: 'java' 
 
repositories { 
    ivy { 
        url = 'http://intranet/custom' 
        ivyPatterns '[module]/[revision]/ivy.xml' 
        artifactPatterns '[module]/[revision]/[artifact](.[ext])' 
    } 
} 
 
dependencies { 
    // Use configuration property in method arguments. 
    testCompile 
      group: 'sample', 
      name: 'logging', 
      version: '1.0', 
      configuration: 'test' 
 
    // Or we use a closure to set the property. 
    testCompile('sample:logging:1.0') { 
        configuration = 'test' 
    } 
} 

Using project dependencies

Gradle 项目可以相互依赖。为了定义这样的依赖,我们使用 project() 方法并使用另一个项目的名称作为参数。 Gradle 会在这个项目中寻找一个默认的依赖配置,并使用这个依赖配置。我们可以使用 configuration属性为每个项目使用不同的依赖配置作为依赖:

apply plugin: 'java' 
 
dependencies { 
    // Define a dependency on projectA 
    // for the code in our project. 
    compile(project(':projectA')) 
 
    // Define a dependency on projectB, 
    // and use specifically the configuration 
    // compile. 
    compile(project(':projectB')) { 
        configuration = 'compile' 
    } 
} 

Using file dependencies

我们可以使用 FileCollection 添加依赖项。我们可以使用 files() 和 fileTree() 方法将依赖项添加到配置中。必须将依赖关系解析为实际的工件。

以下示例使用文件依赖项进行编译配置:

apply plugin: 'java' 
 
dependencies { 
    compile files('spring-core.jar', 'spring-aop.jar') 
    compile fileTree(dir: 'deps', include: '*.jar') 
} 

Using client module dependencies

通常,Gradle 将使用在存储库中找到的依赖项的描述符 XML 文件,以查看需要下载哪些工件和可选的传递依赖项。但是,这些描述符文件可能配置错误,因此,我们可能希望自己覆盖描述符以确保依赖关系正确。为此,我们必须使用 module() 方法来定义依赖项的传递依赖项。然后 Gradle 将使用我们的配置,而不是存储库中模块提供的配置,如下所示:

apply plugin: 'java' 
 
ext { 
    springGroup = 'org.springframework' 
    springRelease = '4.2.3.RELEASE' 
} 
 
dependencies { 
    compile module("$springGroup:spring-context:$springRelease") { 
        dependency("$springGroup:spring-aop:$springRelease") { 
            transitive = false 
      } 
    } 
} 

Using Gradle and Groovy dependencies

当我们开发 Grails 插件和任务时,我们可以定义对当前 Gradle 版本使用的 Gradle API 和 Groovy 库的依赖。我们可以使用 gradleApi() 和 localGroovy() 方法来做到这一点。

以下示例定义了项目的编译依赖配置中的依赖:

apply plugin: 'groovy' 
 
// Dependency configuration for developing 
// Gradle plugins and tasks with Groovy. 
dependencies { 
    // Gradle API available for compile task. 
    compile gradleApi() 
 
    // Groovy libraries used by Gradle version. 
    groovy localGroovy() 
} 

Accessing configuration dependencies

我们可以通过 Project 对象的 configurations 属性访问构建文件或任务中依赖配置的依赖。我们可以使用 dependencies()和 allDependencies()方法来获取依赖的引用,如下:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    runtime "org.springframework:spring-aop:4.2.3.RELEASE" 
} 
 
task dependencyInfo << { 
    println "-- Runtime dependencies --" 
    configurations.runtime.dependencies.each { 
        println "${it.group}:${it.name}:${it.version}" 
    } 
 
    println "-- Runtime allDependencies --" 
    configurations.runtime.allDependencies.each { 
        println "${it.group}:${it.name}:${it.version}" 
    } 
} 

Setting dynamic versions

到目前为止,我们已经为具有完整版本号的依赖项显式设置了版本。要设置最小版本号,我们可以使用特殊的动态版本语法。例如,要将依赖项的版本设置为最低2.1,我们使用版本值 2.1.+ . Gradle 会将依赖关系解析为版本 2.1 之后的最新版本或版本 2.1 本身。在下面的例子中,我们将至少定义一个对 spring-core版本的 3.1的依赖:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    compile(group: 'org.springframework', 
        name: 'spring-core', 
        version: '4.2.+') 
} 

我们还可以使用 latest.integration 引用模块的最新发布版本。我们还可以设置具有最小和最大版本号的版本范围。下表显示了我们可以使用的范围:

范围

说明

[1.0, 2.0]

大于等于 1.0 且小于等于 2.0 的所有版本

[1.0, 2.0[

所有大于等于 1.0 且低于 2.0 的版本

]1.0, 2.0]

所有大于 1.0 且低于或等于 2.0 的版本

]1.0, 2.0[

所有高于 1.0 和低于 2.0 的版本

[1.0, )

所有大于或等于 1.0 的版本

]1.0, )

所有大于 1.0 的版本

(, 2.0]

所有低于或等于 2.0 的版本

(, 2.0[

所有低于 2.0 的版本

以下示例构建文件将使用版本 4.2.3.RELEASE 作为最新版本,该版本大于 4.2 并且小于比 4.3

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    compile(group: 'org.springframework', 
      name: 'spring-core', 
      version: '[4.2, 4.3[') 
} 

Resolving version conflicts

如果我们有一个项目有很多依赖,并且这些依赖具有传递依赖,那么很容易出现版本冲突。如果一个模块依赖于 sample:logging:1.0 而另一个依赖于 sample:logging:2.0,Gradle 将使用默认为最新版本号。

为了改变默认行为,我们设置了依赖配置的 resolutionStrategy 属性。如果出现冲突,我们可以指示 Gradle 使构建失败。这对于调试版本冲突非常有用。

在以下示例构建文件中,如果所有配置都出现版本冲突,我们会指示 Gradle 使构建失败:

apply plugin: 'java' 
 
configurations.all { 
    resolutionStrategy { 
        failOnVersionConflict() 
    } 
} 

要强制某个版本号用于所有依赖项(甚至是传递依赖项),我们可以使用 resolutionStrategy<的force()方法/代码>。使用这种方法,我们可以确保给定模块始终使用首选版本:

apply plugin: 'java' 
 
configurations.all { 
    resolutionStrategy { 
        force('org.springframework:spring-core:4.2.3.RELEASE') 
    } 
} 

Adding optional ANT tasks

我们可以在Gradle 构建文件。 Gradle 使用 Groovy 的 AntBuilder 进行 ANT 集成。但是,如果我们想使用可选的 ANT 任务,我们必须做一些额外的事情,因为可选任务及其依赖项不在 Gradle 类路径中。幸运的是,我们只需要在  build.gradle 文件中定义可选任务的依赖关系,然后我们就可以定义和使用可选的 ANT 任务。

在以下示例中,我们使用 scp ANT 可选任务。我们定义了一个名为 sshAntTask 的新配置,并将依赖项分配给该配置。然后,我们可以定义任务并将 classpath属性设置为配置的 classpath。我们使用 asPath 属性为ANT 任务转换 configurations 类路径。在示例中,我们还将看到如何在脚本运行时询问用户输入。 SSH 密钥文件的 passphrase 是一个秘密,我们不想将其保存在某个文件中,因此我们向用户询问。  System.console() Java 方法返回对控制台的引用,通过 readPassword() 方法,我们可以得到 passphrase, 的值,如下:

// We define a new configuration with the name 'sshAntTask'. 
// This configuration is used to define our dependencies. 
configurations { 
    sshAntTask 
} 
 
repositories { 
    jcenter() 
} 
 
// Assign dependencies to the sshAntTask configuration. 
dependencies { 
    sshAntTask('org.apache.ant:ant-jsch:1.7.1', 'jsch:jsch:0.1.29') 
} 
 
// Sample task which uses the scp ANT optional task. 
task update { 
    description = 'Update files on remote server.' 
 
    // Get passphrase from user input. 
    def console = System.console() 
 
    def passphrase = 
        console.readPassword( 
            '%s: ', 
             'Please enter the passphrase for the keyfile') 
 
    // Redefine scp ANT task, with the classpath 
    // property set to our newly defined 
    // sshAntTask configuration classpath. 
    ant.taskdef( 
      name: 'scp', 
      classname: 'org.apache.tools.ant.taskdefs.optional.ssh.Scp', 
      classpath: configurations.sshAntTask.asPath) 
 
    // Invoke the scp ANT task. 
    // (Use gradle -i update to see the output of the ANT task.) 
    ant.scp( 
      todir: 'mrhaki@servername:/home/mrhaki', 
      keyfile: '${user.home}/.ssh/id_rsa', 
      // Use phassphrase entered by the user. 
      passphrase: passphrase as String, 
      verbose: 'true') { 
      fileset(dir: 'work') { 
        include(name: '**/**') 
      } 
    } 
} 

Using dependency configurations as files

每个依赖配置都实现了 Gradle 的 FileCollection 接口。这意味着如果我们在某处需要文件列表,我们可以使用配置引用。然后使用构成已解析依赖项配置的文件。

让我们创建一个新的构建文件并使用依赖配置作为 from() 方法的值。我们创建一个 Copy类型的新任务并将新配置的所有依赖项 springLibs复制到一个目录:

repositories { 
    jcenter() 
} 
 
configurations { 
    springLibs 
} 
 
dependencies { 
    springLibs('org.springframework:spring-web:4.2.3.RELEASE') 
} 
 
task copyCompileDeps(type: Copy) { 
    from configurations.springLibs 
    into "$buildDir/compileLibs" 
} 

Summary


In this chapter, we discussed dependency management support in Gradle. We also saw how to create a dependency configuration or use dependency configurations provided by a plugin.

为了获得真正的依赖工件及其传递依赖,我们必须定义存储这些文件的存储库。 Gradle 允许使用非常灵活的存储库配置。

最后,我们看到了如何为依赖配置定义实际的依赖。我们讨论了如何解决依赖项之间的版本冲突并在 Gradle 构建中使用这些依赖项。

在下一章中,我们将了解如何为我们的代码运行测试并从我们的构建中执行 Java 应用程序。我们还将讨论如何将我们自己的项目发布到存储库。