如何参数化 Spring Boot Gradle 插件?

How do you parameterize the Spring Boot Gradle plugin?

我们正在寻求从 Maven 迁移到 Gradle,并且已经解决了替换父 POM 概念时可能遇到的大部分挑战。我们还没有弄清楚一个棘手的问题。我们需要指定我们在全局使用的 Spring Boot 的版本,但是我 运行 使用我尝试过的两种解决方案都遇到了无效的构建文件问题:

如果我可以简单地 apply plugin: 'org.springframework.boot' 但 Gradle 找不到插件,所有这一切都会更容易。除了一个微服务外,所有微服务都在 Spring 启动的单一版本上,我们希望能够在可能的情况下进行全局升级。

附加信息

我常用的构建脚本包含在这里:

repositories {
    mavenLocal()
    
    /* Removed our internal repositories */

    jcenter()
    mavenCentral()
}

apply plugin: 'java'
apply plugin: 'jacoco'
apply plugin: 'maven-publish'
apply plugin: 'io.spring.dependency-management'

group = 'nedl-unified-platform'

/* Required to publish Spring Boot microservices to publish to repository */
configurations {
    [apiElements, runtimeElements].each {
        it.outgoing.artifacts.removeIf { it.buildDependencies.getDependencies(null).contains(jar) }
        it.outgoing.artifact(bootJar)
    }
}

java {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
    withJavadocJar()
    withSourcesJar()
}

ext {
    set('springBootVersion', '2.1.17.RELEASE')
    set('springCloudVersion', "Greenwich.SR6")
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

jacoco {
    toolVersion = "0.8.5"
    reportsDir = file("$buildDir/reports/jacoco")
}

test {
    finalizedBy jacocoTestReport // report is always generated after tests run
}

jacocoTestCoverageVerification {
    violationRules {
        rule {
            limit {
                minimum = 0.2
            }
        }
    }
}

jacocoTestReport {
    dependsOn test // tests are required to run before generating the report
    
    reports {
        xml.enabled true
        html.destination file("${reportsDir}/jacocoHtml")
        xml.destination file("${reportsDir}/jacocoReport.xml")
    }
}

tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}

publishing {
    publications {
        maven(MavenPublication) {
            from components.java
        }
    }

    repositories {
        /* excluded for privacy and brevity's sake, our internal Maven repo */
    }
}

这是由我要参数化的项目构建脚本调用的:

plugins {
    id 'org.springframework.boot' version springBootVersion
}

apply from: "https://mycentral.repo/project-common/develop/build.gradle"

dependencies {
    implementation  'org.springframework.boot:spring-boot-starter-actuator'
    implementation  'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
    implementation  'ch.qos.logback:logback-classic'
    implementation  'javax.annotation:javax.annotation-api:1.3.2'
    implementation  'javax.xml.bind:jaxb-api:2.4.0-b180830.0359'
    implementation  'org.glassfish.jaxb:jaxb-runtime:2.4.0-b180830.0438'
    testImplementation  'org.springframework.boot:spring-boot-starter-test'
}

version = '0.0.2-SNAPSHOT'

我不确定我是否完全理解您的问题,但您应该使用 Gradle 共享配置的方式:根项目配置。

与其在每个项目中包含通用构建脚本,不如创建一个全局项目并在此处设置配置。

root
|
| --- projectA
| --- projectB
| --- projectC

根据settings.gradle

include 'projectA'
include 'projectB'
include 'projectC'

在根build.gradle中,设置版本

ext.springBootVersion = '2.1.17.RELEASE'

在使用springBoot的子项目中,比方说projectB,在子项目中应用插件build.gradle

buildscript {
    repositories {
        jcenter()
        mavenCentral()
    }

    dependencies {
        classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
    }
}

apply plugin: 'org.springframework.boot'

我认为这里的差距在于,在 maven 中您有父级的概念 pom,而在 Gradle 中您没有。没有像你说的 1:1 映射到这个,但是你可以在 Gradle 中有插件,并应用一个插件。

最接近的情况是开发自己的 Gradle 插件,您的每个项目都可以应用该插件。然后,您的自定义插件将配置 Spring Boot 以及所有项目通用的其他内容。此插件将定义您希望所有其他项目使用的 Spring 引导版本。

如果自定义插件只关心配置 Spring 引导,那么您不会从中获益太多,它还需要做其他事情。如果您没有足够的经验,可能很难创建 Gradle 插件。你失去了对 build.gradle 所有熟悉的语法,你实际上必须编写代码,(有一些相似之处,但我发现它很难),我会尽可能避免它。

我建议您首先将 spring 启动插件直接应用到您的一个微服务项目,让它工作,然后再做另一个。在完成其中一些之后,您将能够看到它们之间的共同点,以及是否确实值得投资开发一个全球插件。不过你真的需要小心,因为你的全局插件有可能既是福也是祸。它可能会带走维护人员的大量手动工作,但如果你弄错了,他们会很伤心,然后他们会想回到 Maven。

我不确定我是否理解您全局定义的 Spring 版本要求。除非你正在使用 SNAPSHOT dependencies/plugins(最好不要那样做),(或者你的仓库之外的黑魔法 settings.gralde),你将不得不在某处放置一些版本。作为替代方案,您可以创建自己的自定义任务,该任务在 check 生命周期上运行,它将检查 spring (或您的插件)的版本并在不是最新版本时打印警告,并鼓励开发者升级。

额外信息 可以使用属性参数化插件,将 属性 放入 gradle.propertiesspringBootVersion=2.1.17.RELEASE .

这个 example 适合我,虽然我可能不理解所有的限制。

如果我们将 Spring 的版本抽象化为一个固定的 URI(例如,在内部 CI/CD 服务器上),然后在每个 project/repo 的 build.gradle:

buildscript {
    repositories {
        jcenter()
    }

    def SPRING_BOOT_VERSION_URI = 'http://localhost:5151/api-server/spring-boot.txt'
    ext.springBootVersion = new URL(SPRING_BOOT_VERSION_URI).getText().trim()

    dependencies {
        classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
    }
}

apply plugin: 'org.springframework.boot'

apply from: "../common/build.gradle"

我意识到最初的问题是 apply plugin 不起作用,但我不清楚这是否会排除此方法。

最后,请注意,很容易将其扩展为更正式的 JSON 规范(根据团队的需求量身定制)。

如果将其添加到根项目,所有子项目都应该能够从同一组 Spring 启动依赖项中导入。神奇的成分是 allprojects 块:

buildscript {
    repositories {
        maven { url "https://plugins.gradle.org/m2/" }
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

ext {
  springBootVersion = '2.3.4.RELEASE'
}

allprojects {
    apply plugin: 'java'
    apply plugin: 'io.spring.dependency-management'

    dependencyManagement {
        imports {
            mavenBom("org.springframework.boot:spring-boot-dependencies:${springBootVersion}")
        }
    }
}
apply plugin: 'org.springframework.boot'