山西路橋建設(shè)集團(tuán)有限公司網(wǎng)站驚艷的網(wǎng)站設(shè)計(jì)
Gradle
環(huán)境介紹
OpenJDK 17.0.5
Gradle 7.6
示例代碼 fly-gradle
Gradle 項(xiàng)目下文件介紹
如果你的電腦安裝了 gradle,可以使用 gradle init 去初始化一個(gè)新的 gradle 工程,然后使用電腦安裝的 gradle 去執(zhí)行構(gòu)建命令。
但是每個(gè)開發(fā)電腦上的 gradle 版本不一樣,為了統(tǒng)一構(gòu)建環(huán)境,我們可以使用 gradle wrapper 限定項(xiàng)目依賴 gradle 的版本。
# 會(huì)生成 gradle/wrapper/* gradlew gradlew.bat
gradle wrapper --gradle-version7.5.1 --distribution-type bin
.
├── build.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
gradlew 和 gradlew.bat
運(yùn)行項(xiàng)目 wrapper 下定義的 gradle 去構(gòu)建項(xiàng)目。
gradlew 是 macos 和 linux 系統(tǒng)下。
gradlew.bat 是 windows 系統(tǒng)下使用的。
wrapper
wrapper 定義項(xiàng)目依賴那個(gè)版本的 gradle,如果本地 distributionPath 沒有對(duì)應(yīng)版本的 gradle,會(huì)自動(dòng)下載對(duì)應(yīng)版本的 gradle。
# gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
# 如果是國(guó)內(nèi)項(xiàng)目,只需要修改這個(gè)url 就可以提高下載速度
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
GRADLE_USER_HOME 沒有配置的話,默認(rèn)是 ~/.gradle
zipStoreBase 和 zipStorePath 定義了下載的 gradle (gradle-7.6-bin.zip) 存儲(chǔ)的本地路徑。
distributionBase 和 distributionPath 定義下載的 gradle 解壓的本地目錄。
gradlew 實(shí)際是運(yùn)行 gradle-wrapper.jar 中的 main 方法,傳遞給 gradlew 的參數(shù)實(shí)際上也會(huì)傳遞給這個(gè) main 方法。
gradle-wrapper.jar 會(huì)判斷是否下載 wrapper 配置的 gradle,并且將傳遞參數(shù)給下載的 gradle,并運(yùn)行下載的 gralde 進(jìn)行構(gòu)建項(xiàng)目。
升級(jí) wrapper 定義的 gradle 版本
./gradlew wrapper --gradle-version7.6
settings.gradle
pluginManagement {
repositories {
maven {
url="file://${rootDir}/maven/plugin"
}
gradlePluginPortal()
}
plugins {
// spring_boot_version 可以在 gradle.properties 配置
id'org.springframework.boot'version"${spring_boot_version}"
}
}
rootProject.name='fly-gradle'
include'user-manage-service','user-manage-sdk'
include'lib-a'
include'lib-b'
settings.gradle 主要用于配置項(xiàng)目名稱,和包含哪些子項(xiàng)目。
也可以用于配置插件的依賴版本(不會(huì)應(yīng)用到項(xiàng)目中去,除非項(xiàng)目應(yīng)用這個(gè)插件)和插件下載的
build.gradle
build.gradle 是對(duì)某個(gè)項(xiàng)目的配置。配置 jar 依賴關(guān)系,定義或者引入 task 去完成項(xiàng)目構(gòu)建。
gradle.properties
主要用于配置構(gòu)建過(guò)程中用到的變量值。也可以配置一些 gradle 內(nèi)置變量的值,用于修改默認(rèn)構(gòu)建行為。
org.gradle.logging.level=quiet
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.jvmargs=-Xms512m -Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.jvmargs 用來(lái)配置 Daemon 的 JVM 參數(shù),默認(rèn)值是 -Xmx512m "-XX:MaxMetaspaceSize=384m"。
當(dāng)我們的項(xiàng)目比較大的時(shí)候,可能會(huì)由于 JVM 堆內(nèi)存不足導(dǎo)致構(gòu)建失敗,就需要修改此配置。
org.gradle.logging.level 調(diào)整 gradle 的日志級(jí)別。參考 gradle logging 選擇想要的日志級(jí)別。
Gradle Daemon
為加快項(xiàng)目構(gòu)建,gralde 會(huì)啟動(dòng)一個(gè)常駐 JVM 后臺(tái)進(jìn)程去處理構(gòu)建,Daemon 進(jìn)程默認(rèn)三小時(shí)過(guò)期且當(dāng)內(nèi)存壓力大的時(shí)候也會(huì)關(guān)閉。
Gradle 默認(rèn)會(huì)啟用 Daemon 進(jìn)程去構(gòu)建項(xiàng)目。
# 查看 daemon 運(yùn)行狀態(tài)
./gradlew --status
# stop daemon 進(jìn)程
./gradlew --stop
# 重啟 daemon 進(jìn)程
./gradlew --daemon
構(gòu)建生命周期
Gradle 是基于 task 依賴關(guān)系來(lái)構(gòu)建項(xiàng)目的,我們只需要定義 task 和 task 之間的依賴關(guān)系,Gradle 會(huì)保證 task 的執(zhí)行順序。
Gradle 在執(zhí)行 task 之前會(huì)建立 Task Graphs,我們引入的插件和自己構(gòu)建腳本會(huì)往這個(gè) task graph 中添加 task。
Gradle 的構(gòu)建過(guò)程分為三部分:初始化階段、配置階段和執(zhí)行階段。
初始化階段
找到 settings.gradle,執(zhí)行其中代碼
確定有哪些項(xiàng)目需要構(gòu)建,然后對(duì)每個(gè)項(xiàng)目創(chuàng)建 Project 對(duì)象,build.gradle 主要就是配置這個(gè) Project 對(duì)象
// settings.gradle
rootProject.name='basic'
println'在初始化階段執(zhí)行'
配置階段
執(zhí)行 build.gradle 中的配置代碼,對(duì) Project 進(jìn)行配置
執(zhí)行 Task 中的配置段語(yǔ)句
根據(jù)請(qǐng)求執(zhí)行的 task,建立 task graph
println '在配置階段執(zhí)行 Task 中的配置段語(yǔ)句'
tasks.register('configured') {
println '在配置階段執(zhí)行 Task 中的配置段語(yǔ)句'
doFirst {
println '在執(zhí)行階段執(zhí)行'
}
doLast {
println '在執(zhí)行階段執(zhí)行'
}
}
執(zhí)行階段
根據(jù) task graph 執(zhí)行 task 代碼。
依賴管理
Maven 私服配置
我們一般都是多項(xiàng)目構(gòu)建,因此只需要在父項(xiàng)目 build.gradle 配置 repositories。
allprojects {
repositories {
maven {
url "${mavenPublicUrl}"
credentials {
username "${mavenUsername}"
password "${mavenPassword}"
}
}
mavenLocal()
mavenCentral()
}
}
credentials 配置賬號(hào)密碼,當(dāng)私服不需要權(quán)限下載的時(shí)候可以不配置。
Gradle 會(huì)按照配置的倉(cāng)庫(kù)順序查詢依賴下載。
配置依賴來(lái)自某個(gè)目錄
dependencies {
compile files('lib/hacked-vendor-module.jar')
}
dependencies {
compile fileTree('lib')
}
有的時(shí)候第三方庫(kù)沒有 maven 供我們使用,可以使用這個(gè)。
依賴沖突
默認(rèn)依賴沖突
::: tip當(dāng)出現(xiàn)依賴沖突的時(shí)候,gradle 優(yōu)先選擇版本較高的,因?yàn)檩^高版本會(huì)兼容低版本。:::
dependencies {
implementation 'com.google.guava:guava:31.1-jre'
implementation 'com.google.code.findbugs:jsr305:3.0.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}
我們可以執(zhí)行下面命令查看項(xiàng)目依賴的版本:
./gradlew dependency-management:dependencies --configuration compileClasspath
------------------------------------------------------------
Project ':dependency-management'
------------------------------------------------------------
compileClasspath - Compile classpath for source set 'main'.
+--- org.springframework.boot:spring-boot-dependencies:3.0.2
+--- com.google.guava:guava:31.1-jre
| +--- com.google.guava:failureaccess:1.0.1
| +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
| +--- com.google.code.findbugs:jsr305:3.0.2
| +--- org.checkerframework:checker-qual:3.12.0
| +--- com.google.errorprone:error_prone_annotations:2.11.0
| \--- com.google.j2objc:j2objc-annotations:1.3
\--- com.google.code.findbugs:jsr305:3.0.0 -> 3.0.2
我們可以看到,gradle 選擇了 com.google.code.findbugs:jsr305:3.0.2 這個(gè)版本。
強(qiáng)制使用某個(gè)版本
如果我們想使用 com.google.code.findbugs:jsr305:3.0.0 版本
dependencies {
implementation 'com.google.guava:guava:31.1-jre'
implementation 'com.google.code.findbugs:jsr305:3.0.0', {
force = true
}
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}
./gradlew -q dependency-management:dependencyInsight --dependency jsr305 --configuration compileClasspath
com.google.code.findbugs:jsr305:3.0.0 (forced)
Variant compile:
| Attribute Name | Provided | Requested |
|--------------------------------|----------|--------------|
| org.gradle.status | release | |
| org.gradle.category | library | library |
| org.gradle.libraryelements | jar | classes |
| org.gradle.usage | java-api | java-api |
| org.gradle.dependency.bundling | | external |
| org.gradle.jvm.environment | | standard-jvm |
| org.gradle.jvm.version | | 17 |
com.google.code.findbugs:jsr305:3.0.0
\--- compileClasspath
com.google.code.findbugs:jsr305:3.0.2 -> 3.0.0
\--- com.google.guava:guava:31.1-jre
\--- compileClasspath
禁用依賴傳遞
guava 不會(huì)傳遞依賴它依賴的庫(kù)到當(dāng)前庫(kù),可以看到
dependencies {
implementation 'com.google.guava:guava:31.1-jre', {
transitive = false
}
implementation 'com.google.code.findbugs:jsr305:3.0.0'
}
./gradlew dependency-management:dependencies --configuration compileClasspath
------------------------------------------------------------
Project ':dependency-management'
------------------------------------------------------------
compileClasspath - Compile classpath for source set 'main'.
+--- org.springframework.boot:spring-boot-dependencies:3.0.2
+--- com.google.guava:guava:31.1-jre
\--- com.google.code.findbugs:jsr305:3.0.0
可以看到 guava 依賴的 jar 沒有傳遞到當(dāng)前項(xiàng)目中來(lái)。
排除某個(gè)依賴
Guava 依賴的別的 jar 可以傳遞進(jìn)來(lái),而且排除了 findbugs, 項(xiàng)目依賴的版本為 3.0.0。
dependencies {
implementation 'com.google.guava:guava:31.1-jre', {
exclude group: 'com.google.code.findbugs', module: 'jsr305'
}
implementation 'com.google.code.findbugs:jsr305:3.0.0'
}
./gradlew dependency-management:dependencies --configuration compileClasspath
------------------------------------------------------------
Project ':dependency-management'
------------------------------------------------------------
compileClasspath - Compile classpath for source set 'main'.
+--- org.springframework.boot:spring-boot-dependencies:3.0.2
+--- com.google.guava:guava:31.1-jre
| +--- com.google.guava:failureaccess:1.0.1
| +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
| +--- org.checkerframework:checker-qual:3.12.0
| +--- com.google.errorprone:error_prone_annotations:2.11.0
| \--- com.google.j2objc:j2objc-annotations:1.3
\--- com.google.code.findbugs:jsr305:3.0.0
可以看到 guava 傳遞到當(dāng)前項(xiàng)目的依賴少了 findbugs 。
配置依賴之間繼承
configurations {
integrationTestImplementation.extendsFrom testImplementation
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
}
configurations.all {
resolutionStrategy {
force 'org.apache.tomcat.embed:tomcat-embed-core:9.0.43'
}
exclude group: 'org.slf4j', module: 'slf4j-simple'
}
api 和 implementation 區(qū)別
jar b 包含一下依賴
dependencies {
api 'org.apache.commons:commons-lang3:3.12.0'
implementation 'com.google.guava:guava:31.1-jre'
}
項(xiàng)目 a 引入 jar b ,commons-lang3 和 guava 都可以被工程 a 使用,只是二者 scope 不一樣。
api 對(duì)應(yīng) compile,在 工程 a 可以直接使用,編譯可以通過(guò)。
implementation 對(duì)應(yīng) runtime,編譯找不到 guava 中的類。
Task
我們引用的插件實(shí)際是添加 task 到 task graph 中去。
我們知道 build.gradle 實(shí)際是用來(lái)配置對(duì)應(yīng)項(xiàng)目的 org.gradle.api.Project。
因此我們可以在 build.gradle 中引用 org.gradle.api.Project 暴露的屬性。
我們可以在 gradle dsl 和 Project 接口中可以知道可以訪問(wèn)哪些屬性。
tasks 實(shí)際就是 Project 暴露的一個(gè)屬性,因此我們可以使用 tasks 往當(dāng)前項(xiàng)目中注冊(cè) task。
tasks.register('hello') {
doLast {
println 'hello'
}
}
推薦使用 tasks.register 去注冊(cè) task,而不是 tasks.create 去直接創(chuàng)建。
tasks.register 注冊(cè)的 task 只會(huì)在用到的時(shí)候才會(huì)初始化。
Groovy 閉包
Groovy Closures
{ [closureParameters -> ] statements }
閉包的示例
{ item++ }
{ -> item++ }
{ println it }
{ it -> println it }
:::tip
當(dāng)方法的最后一個(gè)參數(shù)是閉包時(shí),可以將閉包放在方法調(diào)用之后。
:::
比如注冊(cè)一個(gè) task 的接口是
register(String var1, Action<? super Task> var2)
tasks.register("task55"){
doFirst {
println "task55"
}
}
tasks.register("task66",{
doFirst {
println "task66"
}
})
Task Type
gradle 已經(jīng)定義好一些 task type,我們可以使用這些 task type 幫助我們完成特定的事情。比如我們想要執(zhí)行某個(gè) shell 命令。
Exec - Gradle DSL Version 7.6
tasks.register("task3", Exec) {
workingDir "$rootDir"
commandLine "ls"
}
Plugin
插件分類
Gradle 有兩種類型的插件 binary plugins and script plugins
二進(jìn)制插件就是封裝好的構(gòu)建邏輯打成 jar 發(fā)布到線上,供別的項(xiàng)目使用。
腳本插件就是一個(gè) *.gradle 文件。
buildSrc
一般我們的項(xiàng)目是多項(xiàng)目構(gòu)建,各個(gè)子項(xiàng)目會(huì)共享一些配置,比如 java 版本,repository 還有 jar publish 到哪里等等。
我們可以將這些統(tǒng)一配置分組抽象為單獨(dú)的插件,子項(xiàng)目引用這個(gè)插件即可。便于維護(hù),不用在各個(gè)子項(xiàng)目都重復(fù)配置相同的東西。
buildSrc 這個(gè)目錄必須在根目錄下,它會(huì)被 gradle 自動(dòng)識(shí)別為一個(gè) composite build,并將其編譯之后放到項(xiàng)目構(gòu)建腳本的 classpath 下。
buildSrc 也可以寫插件,我們可以直接在子項(xiàng)目中使用插件 id 引入。
buildSrc/
├── build.gradle
├── settings.gradle
└── src
├── main
│ ├── groovy
│ │ └── mflyyou.hello2.gradle
│ ├── java
│ │ └── com
│ │ └── mflyyou
│ │ └── plugin
│ │ ├── BinaryRepositoryExtension.java
│ │ ├── BinaryRepositoryVersionPlugin.java
│ │ └── LatestArtifactVersion.java
buildSrc/build.gradle
groovy-gradle-plugin 對(duì)應(yīng)的是使用 groovy 寫插件。
java-gradle-plugin 對(duì)應(yīng) java
plugins {
id 'groovy-gradle-plugin'
id 'java-gradle-plugin'
}
gradlePlugin {
plugins {
helloPlugin {
id = 'com.mflyyou.hello'
implementationClass = 'com.mflyyou.plugin.BinaryRepositoryVersionPlugin'
}
}
}
我們就可以在子項(xiàng)目使用插件
plugins {
id 'com.mflyyou.hello'
id 'mflyyou.hello2'
}
插件使用
Applying plugins with the plugins DSL
plugins {
id 'java'
}
Applying a plugin with the buildscript block
buildscript {
repositories {
gradlePluginPortal()
}
dependencies {
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5'
}
}
apply plugin: 'com.jfrog.bintray'
Applying a script plugin
apply from: 'other.gradle'