iT邦幫忙

2024 iThome 鐵人賽

DAY 5
2

因為後續會頻繁使用 Gradle,在此稍微花一個章節來詳細介紹一下 Gradle。特別感謝我的朋友 Clarke 在這個章節中給予全面的指導與建議:

Gradle 介紹

Gradle 是 Java 生態系統中非常強大的自動化建構工具,類似於 Java 的 Makefile。它可以幫助開發者在專案開發過程中自動化處理各種繁瑣的任務,例如編譯、測試、檢查程式碼、生成文件、清理專案或壓縮檔案,甚至包括部署到伺服器、發布版本、重啟服務,或是發送通知郵件。這些操作都可以透過 Gradle 的 Script 自動完成,讓開發流程更加順暢與高效。

Gradle 擁有以下幾個顯著的特點:

  1. 簡單易上手: Gradle 的語法基於 Groovy,並採用簡潔的 DSL(領域專用語言)來定義建構任務,即使對 Java 不熟悉的開發者也可以輕鬆掌握。此外,Gradle 也支持 Kotlin DSL,為熟悉 Kotlin 的開發者提供了更靈活的選擇。
  2. 豐富的套件生態: Gradle 內建 Plugin 機制,擁有廣泛的套件庫,開發者可以根據需求輕鬆找到合適的套件,或自行開發擴充功能。從後端到前端的多樣套件支持,使 Gradle 成為全方位的構建工具。
  3. 強大的庫支持: 隨著 Java 技術的發展,Gradle 可整合眾多函式庫。例如,使用 JasperReport 生成報表、JavaMail 處理電子郵件、Jetty 內嵌伺服器,甚至透過 JGit 進行版本控管等,這些功能使 Gradle 成為開發者得力的幫手。
  4. 跨平台執行: 只要安裝了 JavaGradle 的程式碼可以在任何作業系統上運行,不需擔心平台之間的兼容性問題。這一跨平台特性使 Gradle 更加靈活,也優於傳統的 Makefile
  5. 增量建構: Gradle 擁有出色的增量建構功能,能夠識別哪些部分發生了變更,僅重新編譯或處理這些部分,大幅提升了大型專案的建構效率。
  6. 依賴管理: Gradle 提供了強大的依賴管理功能,支持多種倉庫(如 Maven CentralJCenter),並且可以解決依賴衝突,處理傳遞性依賴。
  7. 多專案支持: 對於多模組專案,Gradle 可以方便地管理專案間的依賴和建構順序,使開發者能夠靈活地拆分專案並簡化管理。
  8. 靈活的配置管理: Gradle 允許開發者根據環境(如開發測試生產)靈活定義建構邏輯,適應不同場景的需求。

2011 年起,許多 GroovyJava 開發者開始使用 Gradle 作為主要的建構工具。雖然早期版本(1.0 之前)有些 Bug性能問題,但隨著不斷優化,這些問題已經得到解決。Gradle 現已成為 Java 開發的重要工具之一,受到開發者廣泛的喜愛。

特別值得一提的是,Google2013 年Gradle 整合到 Android SDK 建構系統中,這大大鞏固了其在 Java 世界的地位。如今,Gradle 幾乎是 Java 專案建置的標準工具之一。

除了 Android 開發之外,Gradle 在其他領域也被廣泛應用:

  1. Spring Boot: 許多 Spring Boot 專案採用 Gradle 來管理建置流程,充分利用其依賴管理和套件系統。
  2. 微服務架構: Gradle 的多模組管理和靈活配置特性,為微服務開發提供了強大的支持。
  3. 持續整合與部署(CI/CD): Gradle 可以與 JenkinsGitLab CICircleCI 等工具無縫集成,輕鬆實現自動化建構與部署流程。
  4. 大數據專案:HadoopSpark大數據框架下,Gradle 也被廣泛應用,特別是在解決依賴管理上展示了極高的靈活性。

隨著雲端架構和微服務設計的普及,Gradle 作為一個靈活且高效的建構工具,正在成為越來越多開發者的首選。其跨平台、易用性和高效能使得 Gradle 在現代專案開發中的應用日益廣泛。對於希望構建雲原生應用的開發者,熟練掌握 Gradle 不僅能提升開發速度,也能為專案的自動化、擴展性和持續交付提供更好的支持。

Gradle Tasks & Dependencies Base認知

Quarkus 專案的 Gradle 文件中,兩個重要的概念是 任務(Tasks) 和 依賴(Dependencies)。

關於Tasks

Gradle 的任務代表了一個獨立的工作單位,例如編譯程式碼、生成 JAR 檔案、產生 Javadoc 或將檔案發佈到Repository。每個任務可以透過 Gradle 的Plugin或是你自己的建置腳本來定義並執行。

以一個簡單的 Gradle 專案為例,專案結構如下:

gradle-project
├── app
│   ├── build.gradle  // 這裡目前是空的
│   └── ...           // Java 程式碼
├── settings.gradle   // 包含子專案 'app'
├── gradlew
└── gradlew.bat

我們可以透過執行以下指令來列出 app 子專案的所有可用任務:

$ ./gradlew :app:tasks

這會顯示該子專案中當前可運行的任務。例如我們以上章節提到的hello-quarkus範例執行結果如下

  • assemble: 組合專案的輸出,但不執行測試。
  • build: 完整建置專案,包括編譯、執行測試和產生輸出。
  • buildDependents: 建置此專案及所有相依於它的專案。

pal2097@LAPTOP-IFQHQUQJ:/mnt/d/Project/iThome-Quarkus/hello-quarkus$ ./gradlew tasks --all

> Task :tasks

------------------------------------------------------------
Tasks runnable from root project 'hello-quarkus'
------------------------------------------------------------

Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
integrationTestClasses - Assembles integration test classes.
jar - Assembles a jar archive containing the classes of the 'main' feature.
nativeTestClasses - Assembles native test classes.
quarkusGeneratedSourcesClasses - Assembles quarkus generated sources classes.
quarkusTestGeneratedSourcesClasses - Assembles quarkus test generated sources classes.
testClasses - Assembles test classes.

若要執行某個任務,例如build,可以下指令,就能做建置專案任務

./gradlew build

Task執行結果

當 Gradle 執行任務時,會根據不同情況標註任務的執行狀態:

  • EXECUTED:任務成功執行了。
  • UP-TO-DATE:任務的輸入和輸出未變更,因此不需要重新執行。
  • FROM-CACHE:任務的輸出來自於緩存,無需重新計算。
  • SKIPPED:任務被跳過,可能是因為條件未滿足。

如上述提到的build task執行完後,顯示結果如下,up-to-date表程式未異動,所以不需要重新執行

pal2097@LAPTOP-IFQHQUQJ:/mnt/d/Project/iThome-Quarkus/hello-quarkus$ ./gradlew build

BUILD SUCCESSFUL in 3s
11 actionable tasks: 11 up-to-date

關於依賴管理(Dependencies)

依賴管理 是 Gradle 用來管理專案所需要的外部資源或函式庫的機制。這些外部資源可以是 Java 程式庫、第三方工具包,甚至是其他專案。當專案需要使用某些特定功能時,Gradle 會自動下載並安裝所需的依賴。

  • 依賴管理 是軟體開發中極其重要的一部分,尤其是當專案規模變大時。開發者經常需要使用外部函式庫或工具,而手動管理這些資源不僅繁瑣,而且容易出錯。

  • Gradle 的依賴管理系統允許開發者聲明專案需要哪些資源,例如某個版本的 JUnit 測試庫或 Spring Framework,然後 Gradle 會自動解決這些依賴並將它們下載到專案中。

例如在 build.gradle 中,可以使用以下程式碼來添加依賴

  • implementation 表示應用程式在運行時需要 Spring BootWeb Starter
  • testImplementation 表示單元測試需要使用 JUnit
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'junit:junit:4.13.2'
}

任務依賴如何協同工作

Gradle 在構建專案時,它會按照你定義的依賴關係,下載所需的資源,然後執行每一個任務。整個過程可以想像為一個流水線作業,從開始到結束每個步驟都需要依賴前面的步驟來完成。使用如下簡化的流程圖

|--------------------|
|   任務1:clean     |  (清理專案的舊檔案)
|--------------------|      |
       ↓                   (任務2 依賴於 任務1 完成)
|--------------------|
|   任務2:compile   |  (編譯 Java 程式碼)
|--------------------|      |
       ↓                   (任務3 依賴於 任務2 完成)
|--------------------|
|   任務3:test      |  (執行單元測試)
|--------------------|      |
       ↓                   
|--------------------|
|   任務4:build     |  (打包應用程式)
|--------------------|

大多數專案的工作流程:

  1. 清理(clean): 清除專案中舊的建構檔案,以確保接下來的建構過程是全新的。
  2. 編譯(compile): 編譯程式碼,將源碼轉換為機器可執行的二進位文件。
  3. 測試(test): 運行單元測試以驗證程式碼的正確性。
  4. 打包(build): 將編譯過的程式碼和其他資源打包成一個完整的應用程式(例如 JAR 文件)。

上述是較簡單的方式,以下用一個Sample去解釋任務依賴,假設我們有三個任務:task1task2task3,我們將 task3 設置為依賴於 task2,而 task2 又依賴於 task1。這樣,當執行 task3 時,Gradle 會確保 task1task2 依次執行。

  • task3 的依賴:當你運行 gradle task3 時,Gradle 會自動識別出 task3 依賴於 task2,而 task2 又依賴於 task1。因此,執行 task3 之前,task1task2 也會被執行。
  • ependsOn 的作用dependsOn 會確保依賴的任務按順序執行。例如,task3 必須在 task2 完成後執行,而 task2 必須在 task1 完成後執行。這種依賴關係確保了任務之間的協同工作。
tasks.register("task1") {
    doLast {
        println("TASK1: Executing task1")
    }
}

tasks.register("task2") {
    dependsOn("task1") // task2 依賴於 task1
    doLast {
        println("TASK2: Executing task2")
    }
}

tasks.register("task3") {
    dependsOn("task2") // task3 依賴於 task2
    doLast {
        println("TASK3: Executing task3")
    }
}

當你執行 gradle task3,輸出如下:

TASK1: Executing task1
TASK2: Executing task2
TASK3: Executing task3

如果想進一步體會任務的前後執行流程,可以在每個任務中加入 doFirstdoLast。這樣可以更清楚地看到任務在執行過程中的不同階段。

tasks.register("task1") {
    doFirst {
        println("TASK1: Preparing to execute task1")
    }
    doLast {
        println("TASK1: Finished executing task1")
    }
}

tasks.register("task2") {
    dependsOn("task1")
    doFirst {
        println("TASK2: Preparing to execute task2")
    }
    doLast {
        println("TASK2: Finished executing task2")
    }
}

tasks.register("task3") {
    dependsOn("task2")
    doFirst {
        println("TASK3: Preparing to execute task3")
    }
    doLast {
        println("TASK3: Finished executing task3")
    }
}

當你運行 gradle task3 時,輸出的順序將會顯示任務在不同階段的執行狀況:

TASK1: Preparing to execute task1
TASK1: Finished executing task1
TASK2: Preparing to execute task2
TASK2: Finished executing task2
TASK3: Preparing to execute task3
TASK3: Finished executing task3

不過大多狀況下,Gradle 的Plugin(如 java 套件或 io.quarkus 套件)已經幫你封裝了構建流程中的一些預設行為。這些預設行為包含了任務的前置和後置處理,並且這些操作可能已經內建到Plugin提供的任務中。

例如,java 套件和 io.quarkus plugin已經定義好了像 compileJavacompileTestJavatest 等任務,這些任務在內部包含了構建過程中的各種操作,例如編譯、測試和生成構件。在這些預定義的任務中,Gradle 使用了類似 doLast 的方式來封裝每個步驟,但這些步驟對用戶是隱藏的。

Gradle是一個蠻強大的工具,如果要看全貌操作的話,可以到 Gradle的官網(請點我),有一系列的操作解說與練習。


上一篇
建置Hello Quarkus
下一篇
Groovy 語法簡易介紹 與 Gradle 相關設定檔案
系列文
微服務奇兵:30天Quarkus特訓營30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言