Gradle 高级功能篇-ApiHug准备-工具篇-005

  🤗 ApiHug × {Postman|Swagger|Api...} = 快↑ 准√ 省↓

  1. GitHub - apihug/apihug.com: All abou the Apihug   
  2. apihug.com: 有爱,有温度,有质量,有信任
  3. ApiHug - API design Copilot - IntelliJ IDEs Plugin | Marketplace

ApiHug 整个工具链基于 Gradle, 使用 ApiHug 准备工作最先需要学习的就是 gradle. 工欲善其事,必先利其器

概要

作为Java 企业级开发的翘楚, Spring 早在2020 将整个框架编译平台从maven 迁移到gradle: Migrating Spring Boot's Build to Gradleopen in new window, gradle 很多‘高级’功能也得以被 '传播', 但是由于大部分遗留的项目或者库还是用maven, 导致gradle 其实在'国内'被广泛传播还是不多,一方面gradle 能做所有maven 能做的事情,另外一方面 gradle 以编程的方式来组织你的项目结构和编译、打包等流程, 一旦熟练后, 后面做扩展真的非常方便,而且本身gradle设计的方式也非常值得学习。

#Verion Catelog

A version catalog is a list of dependencies, represented as dependency coordinates, that a user can pick from when declaring dependencies in a build script.

很久以前我们是如何控制依赖包的版本:


dependencies {
    implementation("com.google.guava:guava:30.0-jre")
    testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.1")
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
}

这样写带来的坏处:

  1. 每个项目都要copy
  2. 每次需要手动更新每个 build 文件, 导致各个项目的版本可能不一致
  3. group, artifact, version 可能搞错

改进版一, 使用 gradle.properties

guavaVersion=30.0-jre

改进二, 放入ext 中

ext {
   guavaVersion = '30.0-jre'
}

这两种方式都可以这样引用:


dependencies {
    implementation("com.google.guava:guava:${guavaVersion}")
}

随着Kotlin DSL的流行, 在 Android 的世界里有一种方式非常流行: 在buildSrc 里用类型安全的 kotlin 语法添加dependencies :

buildSrc/src/main/kotlin/Libs.kt

object Libs {
   val guava = "com.google.guava:guava:30.0-jre"
}

然后在 build 文件中可以:


build.gradle

dependencies {
    implementation(Libs.guava)
}

最终 version catelog 来了, 首先需要 settings.gradle 里面声明,以启动这个在gradle 7+ 里面启动的功能:


enableFeaturePreview("VERSION_CATALOGS")

然后在你的 settings.gradle 里面声明一个 version catalog, GAV (group, artifact, version) 三套件齐活:


dependencyResolutionManagement {
    versionCatalogs {
        libs {
            library('groovy-core', 'org.codehaus.groovy:groovy:3.0.5')
            library('groovy-json', 'org.codehaus.groovy:groovy-json:3.0.5')
            library('groovy-nio', 'org.codehaus.groovy:groovy-nio:3.0.5')
            library('commons-lang3', 'org.apache.commons', 'commons-lang3').version {
                strictly '[3.8, 4.0['
                prefer '3.9'
            }
        }
    }
}

如何去引用这些lib 呢在 build.gradle 中如此操作:

dependencies {
    implementation libs.groovy.core
    implementation libs.groovy.json
    implementation libs.groovy.nio
}

当然你也可以从一个 toml 风格文件导入:

[versions]
common = "1.4"

[libraries]

my-lib = "com.mycompany:mylib:1.4"
my-other-lib = { module = "com.mycompany:other", version = "1.4" }
my-other-lib2 = { group = "com.mycompany", name = "alternate", version = "1.4" }
mylib-full-format = { group = "com.mycompany", name = "alternate", version = { require = "1.4" } }
[plugins]
short-notation = "some.plugin.id:1.4"
long-notation = { id = "some.plugin.id", version = "1.4" }
reference-notation = { id = "some.plugin.id", version.ref = "common" }

引用:


dependencyResolutionManagement {
    versionCatalogs {
        libs {
            from(files("../gradle/libs.versions.toml"))
        }
    }
}

#Platform

在组织内部通过导入一个 toml 文件解决统一配置版本是个不错的选择,但是如果跨组织?比如你希望你的版本是共享的,像开源的spring依赖第三方包, 你要不将文件传到一个公共的地方,或者像maven 做一个 pom 包分享。version-catalog 就是干这个事的:


plugins {
    id 'version-catalog'
    id 'maven-publish'
}

然后定义你的版本依赖:

catalog {
    // declare the aliases, bundles and versions in this block
    versionCatalog {
        library('my-lib', 'com.mycompany:mylib:1.2')
    }
}

然后使用 maven-publish 或者 ivy-publish 来发布:

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

这个命令会生成一个 libs.versions.toml 文件,并上传,然后其他gradle 应用可以拉下来使用。

“可共享的依赖版本管理” —— 用过 Maven 的小伙伴们可能说,这不就是BOM么。对,这里聊的就是如何使用 gradle 实现 BOM 生成和导入。没用过 Maven 的小伙伴们也不用被劝退,想想在使用 Spring plugin io.spring.dependency-management时,imports.mavenBom到底在做什么,有没有想要了解一下?

在说 BOM 之前,先了解一下 Maven 的一些基本概念。 Maven POM,全名 Project Object Model, 是 Maven 使用中的重要配置文件,xml格式,主要用来导入依赖和进行项目构建。 Maven BOM,全名 Bill Of Materials, 是一种特殊的 POM,主要用来集中管理项目依赖的版本,更加灵活地维护所有依赖的版本信息。 配置好的 BOM,可以放在单个项目中自用,也可以传阅和分享给其他项目进行公用。如下一个标准的spring boot 项目配置open in new window

plugins {
	id 'org.springframework.boot' version '2.7.0'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'java'
}

group = 'com.dearxue.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

ext {
	set('springCloudVersion', "2021.0.3")
	set('testcontainersVersion', "1.17.2")
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.cloud:spring-cloud-starter-config'
	annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.testcontainers:junit-jupiter'
}

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

tasks.named('test') {
	useJUnitPlatform()
}

上面两个插件, 在另外一篇文章中有相信描述,官方文档参考:

pugin文档源码备注
org.springframework.boot2.7.0DOCopen in new window源码open in new window
io.spring.dependency-management1.0.11.REALEASEDOCopen in new window源码open in new window

可以看到 我们导入 org.springframework.boot:spring-boot-starter-web 时候没有指定版本号, 这个是以为 上面两个插件的配合, boot 定义了 spring boot depedencies BOM 版, depedency management 自动导入。

The Java Platform Pluginopen in new window

Platform 是一个特别的包, 不包含源码,只是用来体现对第三方包的依赖,所以被用来解决项目里依赖的统一管理。

Platforms 可以以 Gradle Modu。 Metadata 或者 Maven BOMs 进行发行。

java-platform 插件不能和 java 或者 java-library公用,也就是理论上一个模块要不是 project 要不是 platform。


plugins {
    id 'java-platform'
}

Maven BOM 和 gradle Java platform 主要不一样是, Gradle dependencies 扩展了 constraints用以更细粒度的灵活配置, 佐以runtime/api 进行使用。 API 在使用platform 引入的时候在编译时候使用, runtime 运行时, 举例:


dependencies {
    constraints {
        api 'commons-httpclient:commons-httpclient:3.1'
        runtime 'org.postgresql:postgresql:42.2.5'
    }
}

这里用 constraints 而不是 dependencies , constraints 只有在依赖被直接或者间接引入的时候才生效, 如上面, 如果项目没有依赖 commons-httpclient 那么不会生效, 如果项目引入了 commons-httpclient 并且是 3.0 版本, 不管是直接还是间接的, 那么会强制使用 3.1 版本而不是 3.0

默认防止混淆在platform 里面误操添加 dependency 而不是 constraint, 这样会导致 Gradle 检验错误. 所以如果你想 dependencies 和 constraints, 都可以用, 你可以添加显示的声明:

javaPlatform {
    allowDependencies()
}

当然, 作为一个服务级的 BOM,自然无需从零开始逐条定义,可以直接先 import 框架级的 BOMs,如上例中的Spring boot / Spring cloud / Spring cloud contract / Junit。 但由于需要使用第三方platform bom, 则不得不打开配置约束 ——javaPlatform.allowDependencies。

spring-framework>framework-bomopen in new window 参考framework 源码下面, framework-bom 的使用方法。


plugins {
    id 'maven-publish'
    id 'java-platform'
}

version '0.1.1-SNAPSHOT'

javaPlatform {
    allowDependencies()
}
dependencies {
    api platform('org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE')
    api platform('org.springframework.cloud:spring-cloud-dependencies:Greenwich.SR3')
    api platform('org.springframework.cloud:spring-cloud-contract-dependencies:2.2.3.RELEASE')
    api platform('org.junit:junit-bom:5.3.2')
    constraints {
        api 'com.google.guava:guava:27.0.1-jre'

        api 'ch.vorburger.mariaDB4j:mariaDB4j-springboot:2.4.0'
        api 'org.mariadb.jdbc:mariadb-java-client:2.2.5'

        api 'org.mockito:mockito-core:2.22.0'
        api 'org.mockito:mockito-junit-jupiter:2.22.0'
        api 'org.assertj:assertj-core:3.11.1'
    }
}

publishing {
    repositories {
        maven {
            credentials {
                username = 'xxxx'
                password = 'xxxx'
            }

            def releasesRepoUrl = 'http://xxxxxxx/artifactory/libs-release/'
            def snapshotsRepoUrl = 'http://xxxxxx/artifactory/libs-snapshot/'
            url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
        }
    }

    publications {
        myPlatform(MavenPublication) {
            from components.javaPlatform
        }
    }
}


这个将会生成一个 BOM 有着 <dependencyManagement> 节点, 然 constraints 里面的版本设定将会转换成 <dependencies>; 在使用的时候, 就如我们使用spring boot 无需未每个jar 制定版本:


dependencies {
    // import a BOM
    implementation platform('org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE')

    // define dependencies without versions
    implementation 'com.google.code.gson:gson'
    implementation 'dom4j:dom4j'
}

catelog 和 platform 都可以用来控制版本 那么到底那个更好呢open in new window? 官方解释:

  1. use catalogs to only define dependencies and their versions for projects and to generate type-safe accessors
  2. use platform to apply versions to dependency graph and to affect dependency resolution

catalog 比起 platform 更轻量级, 当然在定义你的platform 时候你也可以用类型安全的 catelog:

plugins {
    id 'java-platform'
}

dependencies {
    constraints {
        api(libs.mylib)
    }
}

#Tool Chain

默认情况下, 运行gradle和编译的JVM是一样的。但是这个不能完全满足我们的需求,在一些特殊情况下可能需要不同的JDK 版本进行打包编译。

Java Toolchain (toolchain)是一个工具链, 一般使用安装的不同的不同版本的 JRE/JDK 来 build。 编译使用javac, test and exec 使用 java ; javadoc 用来生成java 文档, tool chain 是 Java plugins 自己带。


build.gradle
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(11)
        vendor = JvmVendorSpec.ADOPTIUM
    }
}

执行: gradle check 会做这些工作:

  1. 使用toolchain 中定义的JVM 版本来设置 compile、 test 、javadoc 等tasks。
  2. Gradle 检测本地安装的 JVMs
  3. Gradle 选择一个toolchain 里面匹配的 JRE/JDK (本例的 JVM 11)
  4. 如果没有, 自动下载匹配的 JDK 版本: AdoptOpenJDKopen in new window


gradlew.bat -q javaToolchains

 + Options
     | Auto-detection:     Enabled
     | Auto-download:      Enabled

 + Oracle JDK 18.0.1.1+2-6
     | Location:           C:\Program Files\Java\jdk-18.0.1.1
     | Language Version:   18
     | Vendor:             Oracle
     | Is JDK:             true
     | Detected by:        Current JVM

vendor 可以有不同的选择, IBM, Open JDK....

单个任务上也可以指定特定的 JDK 版本:

tasks.withType(JavaCompile).configureEach {
    javaCompiler = javaToolchains.compilerFor {
        languageVersion = JavaLanguageVersion.of(8)
    }
}
task('testsOn14', type: Test) {
    javaLauncher = javaToolchains.launcherFor {
        languageVersion = JavaLanguageVersion.of(14)
    }
}
  1. JavaCompiler 对应 JavaCompile task
  2. JavaLauncher 对应 JavaExec or Test tasks
  3. JavadocTool 对应 Javadoc task

#Build Cache

Gradle build cache 是将编译的结果保存在本地或者远程,用来节省时间, 当然是input 没有被修改情况下。

两种方式启动:

  1. --build-cache 在启动命令上
  2. org.gradle.caching=true 放在 gradle.properties 里

#参考

  1. Tool Chain 官方文档open in new window
  2. Spring Tool Chain 配置open in new window
  3. Sharing dependency versions between projectsopen in new window
  4. Gradle quickie: lazinessopen in new window
  5. The Java Platform Pluginopen in new window
  6. Build Cache

api-hug-contact

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值