Gradle 支持功能的概念:通常情况下,单个库可以分为多个相关但不同的库,其中每个功能都可以与库一起使用。

功能允许组件公开多个相关库,每个库都可以声明自己的依赖项。这些库作为变体公开,类似于主公开其 API 和运行时变体的方式。

这允许多种不同的场景(列表并不详尽):

通过功能选择功能

声明对组件的依赖关系通常是通过提供一组坐标(组、工件、版本也称为 GAV 坐标)来完成的。这使得引擎能够确定我们正在寻找的组件,但是这样的组件可能会提供不同的变体。通常根据用途选择变。例如,我们可能会选择不同的变体来针对组件进行编译(在这种情况下我们需要组件的 API)或在执行代码时(在这种情况下我们需要组件的运行时)。组件的所有变体都提供了许多功能,这些功能都使用 GAV 坐标进行类似的表示。

能力由 GAV 坐标表示,但您必须将其视为功能描述:

  • “我提供 SLF4J 绑定”

  • “我为 MySQL 提供运行时支持”

  • “我提供 Groovy 运行时”

一般来说,有两个组件在图中提供相同的东西是一个问题(它们是冲突的)。

这是一个重要的概念,因为:

  • 默认情况下,变体提供与其组件的 GAV 坐标相对应的功能

  • 依赖图中没有两个变体可以提供相同的功能

  • 可以选择单个组件的多个变体,只要它们提供不同的功能

典型的组件提供具有默认功能的变体。例如,Java 库公开了两个提供相同功能的变体(API 和运行时) 。因此,在依赖关系图中同时包含单个组件的API运行时是错误的。

但是,假设您需要组件的运行时测试装置运行时。然后,只要库的运行时测试装置运行时变体声明不同的功能,就允许这样做。

如果我们这样做,消费者就必须声明两个依赖项:

  • 其一是“主要”功能,即图书馆

  • 关于“测试夹具”功能的一项,要求其能力

虽然解析引擎支持独立于生态系统的多变体组件,但目前只能使用 Java 插件使用 这些功能。

注册功能

可以通过应用java-library插件来声明功能。以下代码说明了如何声明名为 的功能mongodbSupport

build.gradle.kts
sourceSets {
    create("mongodbSupport") {
        java {
            srcDir("src/mongodb/java")
        }
    }
}

java {
    registerFeature("mongodbSupport") {
        usingSourceSet(sourceSets["mongodbSupport"])
    }
}
build.gradle
sourceSets {
    mongodbSupport {
        java {
            srcDir 'src/mongodb/java'
        }
    }
}

java {
    registerFeature('mongodbSupport') {
        usingSourceSet(sourceSets.mongodbSupport)
    }
}

Gradle 会自动为您设置许多内容,其方式与Java 库插件设置配置的方式非常相似。

依赖范围配置的创建方式与主要功能相同:

  • 配置mongodbSupportApi,用于声明此功能的API 依赖项

  • 配置mongodbSupportImplementation,用于声明此功能的实现依赖项

  • 配置mongodbSupportRuntimeOnly,用于声明此功能的仅运行时依赖项

  • 配置mongodbSupportCompileOnly,用于声明此功能的仅编译依赖项

  • 配置mongodbSupportCompileOnlyApi,用于声明此功能的仅编译 API 依赖项

此外,消耗品配置的创建方式与主要功能相同:

  • 配置mongodbSupportApiElements,供消费者用来获取该功能的工件和 API 依赖项

  • 配置mongodbSupportRuntimeElements,消费者使用它来获取该功能的工件和运行时依赖项

功能应该有一个同名的源集。 Gradle 将创建一个Jar任务来捆绑从功能源集构建的类,使用与功能的短横线命名相对应的分类器。

注册功能时 请勿使用源集。此行为将在 Gradle 的未来版本中弃用。

大多数用户只需要关心依赖范围配置,来声明该功能的具体依赖:

build.gradle.kts
dependencies {
    "mongodbSupportImplementation"("org.mongodb:mongodb-driver-sync:3.9.1")
}
build.gradle
dependencies {
    mongodbSupportImplementation 'org.mongodb:mongodb-driver-sync:3.9.1'
}

按照约定,Gradle 将功能名称映射到一个功能,该功能的组和版本分别与主组件的组和版本相同,但其名称是主组件名称后跟 ,后跟-短横线命名的功能名称。

例如,如果组件的组是org.gradle.demo,其名称是provider,其版本是1.0,并且该功能的名称是mongodbSupport,则该功能的变体将具有该org.gradle.demo:provider-mongodb-support:1.0功能。

如果您自己选择功能名称或向变体添加更多功能,建议遵循相同的约定。

发布功能

根据元数据文件格式,发布功能可能会有损失:

  • 使用Gradle 模块元数据,所有内容都会发布,消费者将充分受益于功能

  • 使用 POM 元数据 (Maven),将功能发布为可选依赖项,并使用不同的分类器发布功能工件

  • 使用ivy元数据,功能作为额外配置发布,这些配置不会通过default配置进行扩展

maven-publish仅使用和插件支持发布功能ivy-publish。 Java 库插件将负责为您注册其他变体,因此不需要其他配置,只需常规发布:

build.gradle.kts
plugins {
    `java-library`
    `maven-publish`
}
// ...
publishing {
    publications {
        create("myLibrary", MavenPublication::class.java) {
            from(components["java"])
        }
    }
}
build.gradle
plugins {
    id 'java-library'
    id 'maven-publish'
}
// ...
publishing {
    publications {
        myLibrary(MavenPublication) {
            from components.java
        }
    }
}

添加 javadoc 和源 JAR

与主要的 Javadoc 和源 JAR类似,您可以配置添加的功能,以便它为 Javadoc 和源生成 JAR。

build.gradle.kts
java {
    registerFeature("mongodbSupport") {
        usingSourceSet(sourceSets["mongodbSupport"])
        withJavadocJar()
        withSourcesJar()
    }
}
build.gradle
java {
    registerFeature('mongodbSupport') {
        usingSourceSet(sourceSets.mongodbSupport)
        withJavadocJar()
        withSourcesJar()
    }
}

对功能的依赖

如前所述,功能在发布时可能会有所损失。因此,消费者只能在以下情况下依赖某个功能:

  • 具有项目依赖性(在多项目构建中)

  • Gradle 模块元数据可用,即发布者必须已发布它

  • 在 Ivy 世界中,通过声明对与功能匹配的配置的依赖

消费者可以通过声明所需的功能来指定它需要生产者的特定功能。例如,如果生产者声明“MySQL 支持”功能,如下所示:

build.gradle.kts
group = "org.gradle.demo"

sourceSets {
    create("mysqlSupport") {
        java {
            srcDir("src/mysql/java")
        }
    }
}

java {
    registerFeature("mysqlSupport") {
        usingSourceSet(sourceSets["mysqlSupport"])
    }
}

dependencies {
    "mysqlSupportImplementation"("mysql:mysql-connector-java:8.0.14")
}
build.gradle
group = 'org.gradle.demo'

sourceSets {
    mysqlSupport {
        java {
            srcDir 'src/mysql/java'
        }
    }
}

java {
    registerFeature('mysqlSupport') {
        usingSourceSet(sourceSets.mysqlSupport)
    }
}

dependencies {
    mysqlSupportImplementation 'mysql:mysql-connector-java:8.0.14'
}

然后,消费者可以通过执行以下操作来声明对 MySQL 支持功能的依赖:

build.gradle.kts
dependencies {
    // This project requires the main producer component
    implementation(project(":producer"))

    // But we also want to use its MySQL support
    runtimeOnly(project(":producer")) {
        capabilities {
            requireCapability("org.gradle.demo:producer-mysql-support")
        }
    }
}
build.gradle
dependencies {
    // This project requires the main producer component
    implementation(project(":producer"))

    // But we also want to use its MySQL support
    runtimeOnly(project(":producer")) {
        capabilities {
            requireCapability("org.gradle.demo:producer-mysql-support")
        }
    }
}

这将自动带来mysql-connector-java对运行时类路径的依赖。如果存在多个依赖项,则所有依赖项都会被引入,这意味着可以使用一项功能将对某一功能做出贡献的依赖项分组在一起。

同样,如果使用Gradle Module Metadata发布了具有功能的外部库,则可以依赖该库提供的功能:

build.gradle.kts
dependencies {
    // This project requires the main producer component
    implementation("org.gradle.demo:producer:1.0")

    // But we also want to use its MongoDB support
    runtimeOnly("org.gradle.demo:producer:1.0") {
        capabilities {
            requireCapability("org.gradle.demo:producer-mongodb-support")
        }
    }
}
build.gradle
dependencies {
    // This project requires the main producer component
    implementation('org.gradle.demo:producer:1.0')

    // But we also want to use its MongoDB support
    runtimeOnly('org.gradle.demo:producer:1.0') {
        capabilities {
            requireCapability("org.gradle.demo:producer-mongodb-support")
        }
    }
}

处理互斥变体

使用功能作为处理功能的方式的主要优点是您可以精确地处理变体的兼容性。规则很简单:

依赖图中没有两个变体可以提供相同的功能

我们可以利用这一点来确保每当用户错误配置依赖项时 Gradle 都会失败。考虑这样一种情况,您的库支持 MySQL、Postgres 和 MongoDB,但只允许同时选择其中之一。我们可以通过确保每个特征也提供相同的功能来模拟这种限制,从而使这些特征不可能在同一个图中一起使用。

build.gradle.kts
java {
    registerFeature("mysqlSupport") {
        usingSourceSet(sourceSets["mysqlSupport"])
        capability("org.gradle.demo", "producer-db-support", "1.0")
        capability("org.gradle.demo", "producer-mysql-support", "1.0")
    }
    registerFeature("postgresSupport") {
        usingSourceSet(sourceSets["postgresSupport"])
        capability("org.gradle.demo", "producer-db-support", "1.0")
        capability("org.gradle.demo", "producer-postgres-support", "1.0")
    }
    registerFeature("mongoSupport") {
        usingSourceSet(sourceSets["mongoSupport"])
        capability("org.gradle.demo", "producer-db-support", "1.0")
        capability("org.gradle.demo", "producer-mongo-support", "1.0")
    }
}

dependencies {
    "mysqlSupportImplementation"("mysql:mysql-connector-java:8.0.14")
    "postgresSupportImplementation"("org.postgresql:postgresql:42.2.5")
    "mongoSupportImplementation"("org.mongodb:mongodb-driver-sync:3.9.1")
}
build.gradle
java {
    registerFeature('mysqlSupport') {
        usingSourceSet(sourceSets.mysqlSupport)
        capability('org.gradle.demo', 'producer-db-support', '1.0')
        capability('org.gradle.demo', 'producer-mysql-support', '1.0')
    }
    registerFeature('postgresSupport') {
        usingSourceSet(sourceSets.postgresSupport)
        capability('org.gradle.demo', 'producer-db-support', '1.0')
        capability('org.gradle.demo', 'producer-postgres-support', '1.0')
    }
    registerFeature('mongoSupport') {
        usingSourceSet(sourceSets.mongoSupport)
        capability('org.gradle.demo', 'producer-db-support', '1.0')
        capability('org.gradle.demo', 'producer-mongo-support', '1.0')
    }
}

dependencies {
    mysqlSupportImplementation 'mysql:mysql-connector-java:8.0.14'
    postgresSupportImplementation 'org.postgresql:postgresql:42.2.5'
    mongoSupportImplementation 'org.mongodb:mongodb-driver-sync:3.9.1'
}

在这里,生产者声明了 3 个功能,每个功能对应一个数据库运行时支持:

  • mysql-support提供db-supportmysql-support功能

  • postgres-support提供db-supportpostgres-support功能

  • mongo-support提供db-supportmongo-support功能

然后,如果消费者尝试同时获取postgres-supportmysql-support功能(这也可以传递):

build.gradle.kts
dependencies {
    // This project requires the main producer component
    implementation(project(":producer"))

    // Let's try to ask for both MySQL and Postgres support
    runtimeOnly(project(":producer")) {
        capabilities {
            requireCapability("org.gradle.demo:producer-mysql-support")
        }
    }
    runtimeOnly(project(":producer")) {
        capabilities {
            requireCapability("org.gradle.demo:producer-postgres-support")
        }
    }
}
build.gradle
dependencies {
    implementation(project(":producer"))

    // Let's try to ask for both MySQL and Postgres support
    runtimeOnly(project(":producer")) {
        capabilities {
            requireCapability("org.gradle.demo:producer-mysql-support")
        }
    }
    runtimeOnly(project(":producer")) {
        capabilities {
            requireCapability("org.gradle.demo:producer-postgres-support")
        }
    }
}

依赖关系解析将失败并出现以下错误:

Cannot choose between
   org.gradle.demo:producer:1.0 variant mysqlSupportRuntimeElements and
   org.gradle.demo:producer:1.0 variant postgresSupportRuntimeElements
   because they provide the same capability: org.gradle.demo:producer-db-support:1.0