作为第一级概念的能力
组件提供了许多功能,这些功能通常与用于提供这些功能的软件体系结构正交。例如,一个库可能在单个构件中包含多个特性。但是,这样的库将以单个 GAV(组、构件和版本)坐标发布。这意味着,在单一坐标下,组件的不同“特征”可能共存。
使用 Gradle,显式声明组件提供的功能变得很有趣。为此,Gradle 提供了 能力 的概念。
通常通过组合不同的 capabilities 来构建功能。
在理想的世界中,组件不应该声明对显式 GAV 的依赖,而是根据功能表达它们的需求:
"give me a component which provides logging"
"give me a scripting engine"
"give me a scripting engine that supports Groovy"
通过对 capabilities 建模,依赖管理引擎可以更智能,并在依赖图中有 incompatible capabilities 时告诉您,或者在图中不同模块提供相同的 capability 时要求您选择。
声明外部模块的功能
值得注意的是,Gradle 支持为您构建的组件声明功能,但也支持为外部组件声明功能,以防它们不支持。
例如,如果您的构建文件包含以下依赖项:
Kotlin
Groovy
dependencies {
// This dependency will bring log4:log4j transitively
implementation("org.apache.zookeeper:zookeeper:3.4.9")
// We use log4j over slf4j
implementation("org.slf4j:log4j-over-slf4j:1.7.10")
}
实际上,很难确定您最终会在类路径上使用两个日志记录框架。事实上, zookeeper
会引入 log4j
,而我们要使用的是 log4j-over-slf4j
。我们可以通过添加一条规则来抢先检测冲突,该规则将声明两个日志记录框架提供相同的功能:
Kotlin
Groovy
dependencies {
// Activate the "LoggingCapability" rule
components.all(LoggingCapability::class.java)
}
class LoggingCapability : ComponentMetadataRule {
val loggingModules = setOf("log4j", "log4j-over-slf4j")
override
fun execute(context: ComponentMetadataContext) = context.details.run {
if (loggingModules.contains(id.name)) {
allVariants {
withCapabilities {
// Declare that both log4j and log4j-over-slf4j provide the same capability
addCapability("log4j", "log4j", id.version)
}
}
}
}
}
通过添加此规则,我们将确保 Gradle will 检测冲突并正确失败:
> Could not resolve all files for configuration ':compileClasspath'. > Could not resolve org.slf4j:log4j-over-slf4j:1.7.10. Required by: project : > Module 'org.slf4j:log4j-over-slf4j' has been rejected: Cannot select module with conflict on capability 'log4j:log4j:1.7.10' also provided by [log4j:log4j:1.2.16(compile)] > Could not resolve log4j:log4j:1.2.16. Required by: project : > org.apache.zookeeper:zookeeper:3.4.9 > Module 'log4j:log4j' has been rejected: Cannot select module with conflict on capability 'log4j:log4j:1.2.16' also provided by [org.slf4j:log4j-over-slf4j:1.7.10(compile)]
请参阅 文档的功能部分 了解如何修复功能冲突。
为本地组件声明附加功能
所有组件都有一个 implicit capability 对应于与组件相同的 GAV 坐标。但是,也可以为组件声明额外的explicit capabilities。只要在不同 GAV 坐标上发布的库是同一 API 的alternate implementation,这就很方便:
Kotlin
Groovy
configurations {
apiElements {
outgoing {
capability("com.acme:my-library:1.0")
capability("com.other:module:1.1")
}
}
runtimeElements {
outgoing {
capability("com.acme:my-library:1.0")
capability("com.other:module:1.1")
}
}
}
功能必须附加到 outgoing configurations ,这是组件的 耗材配置 。
此示例表明我们声明了两个功能:
com.acme:my-library:1.0
,对应库的 implicit capabilitycom.other:module:1.1
,对应于该库的另一个功能
值得注意的是我们需要做 1. 因为一旦您开始声明 explicit 功能,就需要声明 all 功能,包括 implicit 功能。
第二个能力可以特定于这个库,或者它可以对应于外部组件提供的能力。在那种情况下,如果 com.other:module
出现在同一个依赖图中,构建将失败并且消费者 必须选择要使用的模块 。
功能发布到 Gradle 模块元数据。但是,它们在 POM 或 Ivy 元数据文件中有no equivalent。因此,在发布此类组件时,Gradle 会警告您此功能仅适用于 Gradle 消费者:
Maven publication 'maven' contains dependencies that cannot be represented in a published pom file. - Declares capability com.acme:my-library:1.0 - Declares capability com.other:module:1.1