🤗 ApiHug × {Postman|Swagger|Api...} = 快↑ 准√ 省↓
- GitHub - apihug/apihug.com: All abou the Apihug
- apihug.com: 有爱,有温度,有质量,有信任
- 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")
}
这样写带来的坏处:
- 每个项目都要copy
- 每次需要手动更新每个 build 文件, 导致各个项目的版本可能不一致
- 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.boot | 2.7.0 | DOCopen in new window | 源码open in new window |
io.spring.dependency-management | 1.0.11.REALEASE | DOCopen 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? 官方解释:
- use catalogs to only define dependencies and their versions for projects and to generate type-safe accessors
- 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
会做这些工作:
- 使用toolchain 中定义的JVM 版本来设置 compile、 test 、javadoc 等tasks。
- Gradle 检测本地安装的 JVMs
- Gradle 选择一个toolchain 里面匹配的 JRE/JDK (本例的 JVM 11)
- 如果没有, 自动下载匹配的 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)
}
}
- JavaCompiler 对应 JavaCompile task
- JavaLauncher 对应 JavaExec or Test tasks
- JavadocTool 对应 Javadoc task
#Build Cache
Gradle build cache 是将编译的结果保存在本地或者远程,用来节省时间, 当然是input 没有被修改情况下。
两种方式启动:
--build-cache
在启动命令上org.gradle.caching=true
放在 gradle.properties 里
#参考
- Tool Chain 官方文档open in new window
- Spring Tool Chain 配置open in new window
- Sharing dependency versions between projectsopen in new window
- Gradle quickie: lazinessopen in new window
- The Java Platform Pluginopen in new window
- Build Cache