Declaring dependencies
在查看依赖项声明本身之前,需要定义依赖项配置的概念.
What are dependency configurations
为Gradle项目声明的每个依赖项都适用于特定范围. 例如,某些依赖项应用于编译源代码,而其他依赖项仅需要在运行时可用. Gradle在Configuration的帮助下表示依赖项的范围. 每个配置都可以通过唯一的名称来标识.
许多Gradle插件都会向您的项目添加预定义的配置. 例如,Java插件添加了配置,以表示其源代码编译,执行测试等所需的各种类路径. 有关示例,请参见Java插件章节 .

有关使用配置导航,检查和后处理分配的依赖项的元数据和工件的更多示例,请查看解析结果API .
Configuration inheritance and composition
一个配置可以扩展其他配置以形成继承层次结构. 子配置继承为其任何超配置声明的整个依赖项集.
Gradle核心插件(如Java插件 )大量使用配置继承. 例如, testImplementation
配置扩展了implementation
配置. 配置层次结构有一个实际目的:编译测试需要在编写测试类所需的依赖之上加上被测试源代码的依赖. 如果将其类导入到生产源代码中,则使用JUnit编写和执行测试代码的Java项目也需要Guava.

在testImplementation
, testImplementation
和implementation
配置通过调用Configuration.extendsFrom(org.gradle.api.artifacts.Configuration [])形成继承层次结构. 配置可以扩展任何其他配置,无论其在构建脚本或插件中的定义如何.
假设您想编写一套烟雾测试. 每个冒烟测试都会进行HTTP调用以验证Web服务端点. 作为基础测试框架,该项目已使用JUnit. 您可以定义一个名为smokeTest
的新配置,该配置从testImplementation
配置扩展以重用现有的测试框架依赖关系.
configurations {
smokeTest.extendsFrom testImplementation
}
dependencies {
testImplementation 'junit:junit:4.12'
smokeTest 'org.apache.httpcomponents:httpclient:4.5.5'
}
val smokeTest by configurations.creating {
extendsFrom(configurations.testImplementation.get())
}
dependencies {
testImplementation("junit:junit:4.12")
smokeTest("org.apache.httpcomponents:httpclient:4.5.5")
}
Resolvable and consumable configurations
从历史上看,配置一直是Gradle中依赖关系解析的根本. 最后,我们要有所作为的是消费者和生产者之间 . 为此,配置至少用于三个不同方面:
-
声明依赖
-
作为使用者 ,解决文件的一系列依赖关系
-
作为生产者 ,将工件及其依赖项公开以供其他项目使用(此类消耗性配置通常表示生产者向其消费者提供的变体 )
例如,如果我想表达我的应用app
应用app
依赖于库lib
,那么我们至少需要一种配置:
configurations {
// declare a "configuration" named "someConfiguration"
someConfiguration
}
dependencies {
// add a project dependency to the "someConfiguration" configuration
someConfiguration project(":lib")
}
// declare a "configuration" named "someConfiguration"
val someConfiguration by configurations.creating
dependencies {
// add a project dependency to the "someConfiguration" configuration
someConfiguration(project(":lib"))
}
配置可以扩展其他配置,以继承其依赖性. 但是,上面的代码没有告诉任何有关使用者的信息 . 特别是,它没有告诉您配置的用途 . 假设lib
是一个Java库:它可以公开不同的内容,例如其API,实现或测试装置. 如果要解决app
的依赖关系,我们需要知道我们正在执行哪种任务(针对lib
的API进行编译,执行应用程序,编译测试等). 为此,您经常会找到伴随的配置,这些配置旨在明确声明其用法:
configurations {
// declare a configuration that is going to resolve the compile classpath of the application
compileClasspath.extendsFrom(someConfiguration)
// declare a configuration that is going to resolve the runtime classpath of the application
runtimeClasspath.extendsFrom(someConfiguration)
}
configurations {
// declare a configuration that is going to resolve the compile classpath of the application
compileClasspath.extendsFrom(someConfiguration)
// declare a configuration that is going to resolve the runtime classpath of the application
runtimeClasspath.extendsFrom(someConfiguration)
}
在此阶段,我们有3种不同的配置,它们已经有不同的目标:
-
someConfiguration
声明我的应用程序的依赖关系. 这只是一个我们在其中声明依赖项列表的存储桶. -
compileClasspath
和runtimeClasspath
是要解析的配置:解析后,它们应分别包含应用程序的编译类路径和运行时类路径.
实际上,这在Configuration
类型上由canBeResolved
标志表示. 可以解析的配置是我们可以为其计算依赖关系图的配置,因为它包含实现解析所需的所有必要信息. 也就是说,我们将计算一个依赖图,解析图中的组件,并最终获得工件. canBeResolved
设置为false
配置并不意味着可以解决. 这样的配置仅用于声明依赖项 . 原因是根据用法(编译类路径,运行时类路径),它可以解析为不同的图. 尝试解析canBeResolved
设置为false
的配置是false
. 在某种程度上,这类似于不应实例化的抽象类 ( canBeResolved
= false),以及扩展了抽象类的具体类( canBeResolved
= true). 可解析的配置将扩展至少一个不可解析的配置(并且可以扩展多个).
另一方面,在图书馆项目方面( 生产者 ),我们还使用配置来表示可以消耗的东西. 例如,该库可以公开一个API或运行时,并且我们会将工件附加到一个或多个. 通常情况下,进行编译lib
,我们需要的API lib
,但我们并不需要它的运行时依赖. 因此, lib
项目将公开一个apiElements
配置,该配置面向正在寻找其API的消费者. 这样的配置将是消耗性的,但并不意味着必须解决. 这是通过Configuration
的canBeConsumed标志表示的:
configurations {
// A configuration meant for consumers that need the API of this component
exposedApi {
// This configuration is an "outgoing" configuration, it's not meant to be resolved
canBeResolved = false
// As an outgoing configuration, explain that consumers may want to consume it
canBeConsumed = true
}
// A configuration meant for consumers that need the implementation of this component
exposedRuntime {
canBeResolved = false
canBeConsumed = true
}
}
configurations {
// A configuration meant for consumers that need the API of this component
create("exposedApi") {
// This configuration is an "outgoing" configuration, it's not meant to be resolved
isCanBeResolved = false
// As an outgoing configuration, explain that consumers may want to consume it
isCanBeConsumed = true
}
// A configuration meant for consumers that need the implementation of this component
create("exposedRuntime") {
isCanBeResolved = false
isCanBeConsumed = true
}
}
简而言之,配置角色由canBeResolved
和canBeConsumed
标志组合确定:
配置角色 |
可以解决 |
可以食用 |
依赖桶 |
false |
false |
解决某些用途 |
true |
false |
暴露给消费者 |
false |
true |
旧版,请勿使用 |
true |
true |
为了向后兼容,这些标志均具有true
作为默认值,但作为插件作者,您应始终为这些标志确定正确的值,否则可能会偶然引入解析错误.
Defining custom configurations
您可以自己定义配置,即所谓的自定义配置 . 定制配置对于分离专用目的所需的依赖项范围很有用.
比方说,你想声明的依赖碧玉Ant任务为不应该在classpath最终编译源代码预编译JSP文件的目的. 通过引入自定义配置并在任务中使用它来实现该目标相当简单.
configurations {
jasper
}
repositories {
mavenCentral()
}
dependencies {
jasper 'org.apache.tomcat.embed:tomcat-embed-jasper:9.0.2'
}
task preCompileJsps {
doLast {
ant.taskdef(classname: 'org.apache.jasper.JspC',
name: 'jasper',
classpath: configurations.jasper.asPath)
ant.jasper(validateXml: false,
uriroot: file('src/main/webapp'),
outputDir: file("$buildDir/compiled-jsps"))
}
}
val jasper by configurations.creating
repositories {
mavenCentral()
}
dependencies {
jasper("org.apache.tomcat.embed:tomcat-embed-jasper:9.0.2")
}
tasks.register("preCompileJsps") {
doLast {
ant.withGroovyBuilder {
"taskdef"("classname" to "org.apache.jasper.JspC",
"name" to "jasper",
"classpath" to jasper.asPath)
"jasper"("validateXml" to false,
"uriroot" to file("src/main/webapp"),
"outputDir" to file("$buildDir/compiled-jsps"))
}
}
}
项目的配置由configurations
对象管理. 配置具有名称,并且可以互相扩展. 要了解有关此API的更多信息,请查看ConfigurationContainer .
Different kinds of dependencies
Module dependencies
模块依赖性是最常见的依赖性. 它们引用存储库中的模块.
dependencies {
runtimeOnly group: 'org.springframework', name: 'spring-core', version: '2.5'
runtimeOnly 'org.springframework:spring-core:2.5',
'org.springframework:spring-aop:2.5'
runtimeOnly(
[group: 'org.springframework', name: 'spring-core', version: '2.5'],
[group: 'org.springframework', name: 'spring-aop', version: '2.5']
)
runtimeOnly('org.hibernate:hibernate:3.0.5') {
transitive = true
}
runtimeOnly group: 'org.hibernate', name: 'hibernate', version: '3.0.5', transitive: true
runtimeOnly(group: 'org.hibernate', name: 'hibernate', version: '3.0.5') {
transitive = true
}
}
dependencies {
runtimeOnly(group = "org.springframework", name = "spring-core", version = "2.5")
runtimeOnly("org.springframework:spring-aop:2.5")
runtimeOnly("org.hibernate:hibernate:3.0.5") {
isTransitive = true
}
runtimeOnly(group = "org.hibernate", name = "hibernate", version = "3.0.5") {
isTransitive = true
}
}
有关更多示例和完整参考,请参阅API文档中的DependencyHandler类.
Gradle为模块依赖性提供了不同的表示法. 有一个字符串符号和一个映射符号. 模块依赖性具有允许进一步配置的API. 看一看ExternalModuleDependency,以了解有关API的全部信息. 该API提供了属性和配置方法. 通过字符串符号,您可以定义属性的子集. 使用地图符号,您可以定义所有属性. 要使用映射或字符串表示法访问完整的API,可以将单个依赖项与闭包一起分配给配置.
✨
|
如果声明模块依赖性,Gradle将在存储库中查找模块元数据文件( |
✨
|
在Maven中,一个模块只能有一个工件. 在Gradle和Ivy中,一个模块可以具有多个工件. 每个工件可以具有不同的依赖关系集. |
File dependencies
项目有时不依赖于二进制存储库产品(例如JFrog Artifactory或Sonatype Nexus)来托管和解决外部依赖项. 通常的做法是将这些依赖项托管在共享驱动器上,或者将其与项目源代码一起放入版本控制中. 这些依赖项称为文件依赖项 ,原因是它们表示文件没有附加任何元数据 (例如有关传递性依赖项,来源或其作者的信息).

以下示例从ant
, libs
和tools
目录解析文件依赖性.
configurations {
antContrib
externalLibs
deploymentTools
}
dependencies {
antContrib files('ant/antcontrib.jar')
externalLibs files('libs/commons-lang.jar', 'libs/log4j.jar')
deploymentTools(fileTree('tools') { include '*.exe' })
}
configurations {
create("antContrib")
create("externalLibs")
create("deploymentTools")
}
dependencies {
"antContrib"(files("ant/antcontrib.jar"))
"externalLibs"(files("libs/commons-lang.jar", "libs/log4j.jar"))
"deploymentTools"(fileTree("tools") { include("*.exe") })
}
在代码示例中可以看到,每个依赖项都必须定义其在文件系统中的确切位置. 创建文件引用的最主要方法是Project.files(java.lang.Object….) , ProjectLayout.files(java.lang.Object….)和Project.fileTree(java.lang.Object).或者,您可以还以平面目录存储库的形式定义一个或多个文件依赖项的源目录.
文件依赖性使您可以直接将一组文件添加到配置中,而无需先将它们添加到存储库中. 如果您无法或不想将某些文件放置在存储库中,这将很有用. 或者,如果您根本不想使用任何存储库来存储依赖项.
要将某些文件添加为配置的依赖项,只需将文件集合作为依赖项传递:
dependencies {
runtimeOnly files('libs/a.jar', 'libs/b.jar')
runtimeOnly fileTree('libs') { include '*.jar' }
}
dependencies {
runtimeOnly(files("libs/a.jar", "libs/b.jar"))
runtimeOnly(fileTree("libs") { include("*.jar") })
}
文件依赖项不包含在项目的已发布依赖项描述符中. 但是,文件依赖关系包含在同一构建中的可传递项目依赖关系中. 这意味着它们不能在当前版本之外使用,但可以在同一版本内使用.
您可以声明产生文件依赖性的任务. 例如,当文件是由生成生成的时,您可能会这样做.
dependencies {
implementation files("$buildDir/classes") {
builtBy 'compile'
}
}
task compile {
doLast {
println 'compiling classes'
}
}
task list(dependsOn: configurations.compileClasspath) {
doLast {
println "classpath = ${configurations.compileClasspath.collect { File file -> file.name }}"
}
}
dependencies {
implementation(files("$buildDir/classes") {
builtBy("compile")
})
}
tasks.register("compile") {
doLast {
println("compiling classes")
}
}
tasks.register("list") {
dependsOn(configurations["compileClasspath"])
doLast {
println("classpath = ${configurations["compileClasspath"].map { file: File -> file.name }}")
}
}
$ gradle -q list compiling classes classpath = [classes]
Versioning of file dependencies
It is recommended to clearly express the intention and a concrete version for file dependencies.
File dependencies are not considered by Gradle’s version conflict resolution.
Therefore, it is extremely important to assign a version to the file name to indicate the distinct set of changes shipped with it.
For example commons-beanutils-1.3.jar
lets you track the changes of the library by the release notes.
因此,项目的依存关系更易于维护和组织. 通过分配的版本发现潜在的API不兼容要容易得多.
Project dependencies
软件项目通常将软件组件分解为模块,以提高可维护性并防止强耦合. 模块可以定义彼此之间的依赖关系,以在同一项目中重用代码.

Gradle可以对模块之间的依赖关系进行建模. 这些依赖项称为项目依赖项,因为每个模块均由Gradle项目表示.
dependencies {
implementation project(':shared')
}
dependencies {
implementation(project(":shared"))
}
在运行时,该构建会自动确保以正确的顺序构建项目依赖项,并将其添加到类路径中进行编译. " 创作多项目构建 "一章讨论了如何设置和配置多项目构建.
有关更多信息,请参见ProjectDependency的API文档.
以下示例从web-service
项目中声明对utils
和api
项目的依赖关系. 方法Project.project(java.lang.String)通过路径创建对特定子项目的引用.
project(':web-service') {
dependencies {
implementation project(':utils')
implementation project(':api')
}
}
project(":web-service") {
dependencies {
"implementation"(project(":utils"))
"implementation"(project(":api"))
}
}
Gradle distribution-specific dependencies
Gradle API dependency
您可以使用DependencyHandler.gradleApi()方法声明对当前版本的Gradle API的依赖关系 . 在开发自定义Gradle任务或插件时,这很有用.
dependencies {
implementation gradleApi()
}
dependencies {
implementation(gradleApi())
}
Gradle TestKit dependency
您可以使用DependencyHandler.gradleTestKit()方法声明对当前版本的Gradle的TestKit API的依赖关系 . 这对于编写和执行Gradle插件和构建脚本的功能测试很有用.
dependencies {
testImplementation gradleTestKit()
}
dependencies {
testImplementation(gradleTestKit())
}
TestKit章节通过示例解释了TestKit的用法.
Local Groovy dependency
您可以使用DependencyHandler.localGroovy()方法声明与Gradle一起分发的Groovy的依赖关系 . 在Groovy中开发自定义Gradle任务或插件时,这很有用.
dependencies {
implementation localGroovy()
}
dependencies {
implementation(localGroovy())
}
Documenting dependencies
plugins {
id 'java-library'
}
repositories {
jcenter()
}
dependencies {
implementation('org.ow2.asm:asm:7.1') {
because 'we require a JDK 9 compatible bytecode generator'
}
}
plugins {
`java-library`
}
repositories {
jcenter()
}
dependencies {
implementation("org.ow2.asm:asm:7.1") {
because("we require a JDK 9 compatible bytecode generator")
}
}
Example: Using the dependency insight report with custom reasons
gradle -q dependencyInsight --dependency asm
输出> gradle -q dependencyInsight --dependency asm org.ow2.asm:asm:7.1 variant "compile" [ org.gradle.status = release (not requested) org.gradle.usage = java-api org.gradle.libraryelements = jar (compatible with: classes) org.gradle.category = library (not requested) Requested attributes not found in the selected variant: org.gradle.dependency.bundling = external org.gradle.jvm.version = 11 ] Selection reasons: - Was requested : we require a JDK 9 compatible bytecode generator org.ow2.asm:asm:7.1 \--- compileClasspath A web-based, searchable dependency report is available by adding the --scan option.
Resolving specific artifacts from a module dependency
每当Gradle尝试从Maven或Ivy存储库解析模块时,它都会查找元数据文件和默认工件文件JAR. 如果这些工件文件都无法解析,则构建将失败. 在某些情况下,您可能需要调整Gradle解析依赖项工件的方式.
-
依赖项仅提供非标准工件,没有任何元数据(例如ZIP文件).
-
模块元数据声明了多个工件,例如,作为常春藤依赖描述符的一部分.
-
您只想下载特定的工件,而无需在元数据中声明任何传递依赖项.
Gradle是一个多语言构建工具,不仅限于解析Java库. 假设您想使用JavaScript作为客户端技术来构建Web应用程序. 大多数项目将外部JavaScript库检入版本控制. 外部JavaScript库与可重用Java库没有什么不同,那么为什么不从存储库中下载它呢?
Google托管库是流行的开源JavaScript库的发行平台. 借助纯工件符号,您可以下载JavaScript库文件,例如JQuery. @
字符将依赖项的坐标与工件的文件扩展名分开.
repositories {
ivy {
url 'https://ajax.googleapis.com/ajax/libs'
patternLayout {
artifact '[organization]/[revision]/[module].[ext]'
}
metadataSources {
artifact()
}
}
}
configurations {
js
}
dependencies {
js 'jquery:jquery:3.2.1@js'
}
repositories {
ivy {
url = uri("https://ajax.googleapis.com/ajax/libs")
patternLayout {
artifact("[organization]/[revision]/[module].[ext]")
}
metadataSources {
artifact()
}
}
}
configurations {
create("js")
}
dependencies {
"js"("jquery:jquery:3.2.1@js")
}
某些模块会运送同一工件的不同"风味",或者它们会发布属于特定模块版本但用途不同的多个工件. Java库通常会发布带有已编译类文件的工件,另一个库中仅包含源代码,而第三个库中包含Javadocs.
在JavaScript中,库可能以未压缩或缩小的工件形式存在. 在Gradle中,特定的工件标识符称为分类器 ,该术语通常在Maven和Ivy依赖管理中使用.
假设我们要下载JQuery库的精简构件,而不是未压缩的文件. 您可以提供分类器min
作为依赖项声明的一部分.
repositories {
ivy {
url 'https://ajax.googleapis.com/ajax/libs'
patternLayout {
artifact '[organization]/[revision]/[module](.[classifier]).[ext]'
}
metadataSources {
artifact()
}
}
}
configurations {
js
}
dependencies {
js 'jquery:jquery:3.2.1:min@js'
}
repositories {
ivy {
url = uri("https://ajax.googleapis.com/ajax/libs")
patternLayout {
artifact("[organization]/[revision]/[module](.[classifier]).[ext]")
}
metadataSources {
artifact()
}
}
}
configurations {
create("js")
}
dependencies {
"js"("jquery:jquery:3.2.1:min@js")
}
Supported Metadata formats
外部模块依赖关系需要模块元数据(因此,Gradle通常可以确定模块的传递依赖关系). 为此,Gradle支持不同的元数据格式.
您还可以调整将在存储库定义中查找哪种格式.
POM files
Gradle本机支持Maven POM文件 . 值得注意的是,默认情况下,Gradle将首先查找POM文件,但是如果该文件包含特殊标记,则Gradle将改为使用Gradle Module元数据 .
Ivy files
同样,Gradle支持Apache Ivy元数据文件 . 同样,Gradle将首先查找ivy.xml
文件,但是如果该文件包含特殊标记,则Gradle将改为使用Gradle模块元数据 .