如何参数化 Spring Boot Gradle 插件?
How do you parameterize the Spring Boot Gradle plugin?
我们正在寻求从 Maven 迁移到 Gradle,并且已经解决了替换父 POM 概念时可能遇到的大部分挑战。我们还没有弄清楚一个棘手的问题。我们需要指定我们在全局使用的 Spring Boot 的版本,但是我 运行 使用我尝试过的两种解决方案都遇到了无效的构建文件问题:
- 我尝试将
plugins { id 'org.springframework.boot' version '2.1.17.RELEASE' }
声明放在通用构建脚本中。 构建错误、“只有项目和设置构建脚本可以包含插件 {} 块。”
- 我尝试调用公共构建文件来指定
springBootVersion
参数并在插件声明中使用它。 构建错误,“在插件{}块之前只允许构建脚本{}和其他插件{}脚本块,不允许其他语句”
如果我可以简单地 apply plugin: 'org.springframework.boot'
但 Gradle 找不到插件,所有这一切都会更容易。除了一个微服务外,所有微服务都在 Spring 启动的单一版本上,我们希望能够在可能的情况下进行全局升级。
附加信息
- 我有大约 40 个微服务以及这些服务使用的一些库
- 它们每个都有单独的存储库,因此正常的 parent/child 方法不起作用
- Maven 父 POM 允许您将该 POM 发布为它自己的资源,并且在 Gradle
中没有 1:1 等效功能
- Gradle
pluginManagement
概念对我们也不起作用,因为它解决了 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.properties
为 springBootVersion=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'
我们正在寻求从 Maven 迁移到 Gradle,并且已经解决了替换父 POM 概念时可能遇到的大部分挑战。我们还没有弄清楚一个棘手的问题。我们需要指定我们在全局使用的 Spring Boot 的版本,但是我 运行 使用我尝试过的两种解决方案都遇到了无效的构建文件问题:
- 我尝试将
plugins { id 'org.springframework.boot' version '2.1.17.RELEASE' }
声明放在通用构建脚本中。 构建错误、“只有项目和设置构建脚本可以包含插件 {} 块。” - 我尝试调用公共构建文件来指定
springBootVersion
参数并在插件声明中使用它。 构建错误,“在插件{}块之前只允许构建脚本{}和其他插件{}脚本块,不允许其他语句”
如果我可以简单地 apply plugin: 'org.springframework.boot'
但 Gradle 找不到插件,所有这一切都会更容易。除了一个微服务外,所有微服务都在 Spring 启动的单一版本上,我们希望能够在可能的情况下进行全局升级。
附加信息
- 我有大约 40 个微服务以及这些服务使用的一些库
- 它们每个都有单独的存储库,因此正常的 parent/child 方法不起作用
- Maven 父 POM 允许您将该 POM 发布为它自己的资源,并且在 Gradle 中没有 1:1 等效功能
- Gradle
pluginManagement
概念对我们也不起作用,因为它解决了 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.properties
为 springBootVersion=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'