有了Gradle,還會選Maven嗎?

2022-01-12 InfoQ 架構頭條
Gradle 在隨行付標準化實踐:一行代碼帶來的變革!

現在許多人還在為使用 Maven 還是 Gradle 而糾結。如果關注過《Maven 權威指南》作者許曉斌老師在 InfoQ 中發表的文章:《Maven 實戰(六)——Gradle,構建工具的未來?》(http://www.infoq.com/cn/news/2011/04/xxb-maven-6-gradle),那麼一定會有同感:Gradle 太靈活,可能會造成不可控。文章中的原話是:

「Gradle 的另外一個問題就是它太靈活了,雖然它支持約定優於配置,不過從本文你也看到了,破壞約定是多麼容易的事情。人都喜歡自由,愛自定義,覺得自己的需求是多麼的特別,可事實上,從 Maven 的流行來看,幾乎 95% 以上的情況你不需要自行擴展,如果你這麼做了,只會讓構建變得難以理解。從這個角度來看,自由是把雙刃劍,Gradle 給了你足夠的自由,約定優於配置只是它的一個選項而已,這初看起來很誘人,卻也可能使其重蹈 Ant 的覆轍。」

首先看一下我們最初使用 Gradle 構建 Spring Cloud 項目的 build.gradle 的寫法:

buildscript {    ext {        springCloudVersion = 'Edgware.SR3'        springBootVersion = '1.5.9.RELEASE'        REPOSITORY_HOME = "http://maven.aliyun.com"    }    repositories {        maven { url "${REPOSITORY_HOME}/nexus/content/groups/public" }    }    dependencies {        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")    }}apply plugin: 'maven'apply plugin: 'java'apply plugin: 'org.springframework.boot'if (JavaVersion.current().isJava8Compatible()) {    allprojects {        tasks.withType(Javadoc) {            options.encoding = 'UTF-8'            options.addStringOption('Xdoclint:none', '-quiet') // 關閉 JDK1.8 的 doclint 特性        }    }}// 導入 Spring Cloud 依賴dependencyManagement {  imports {    mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"  }}dependencyManagement {  resolutionStrategy {    // 檢查遠程依賴是否存在更新    cacheChangingModulesFor 0, 'seconds'    cacheChangingModulesFor 0, 'seconds' // 修改本地緩存策略  }}sourceCompatibility = JavaVersion.VERSION_1_8targetCompatibility = JavaVersion.VERSION_1_8bootRepackage {  // 默認只打普通 jar 包  enabled = false}// 打包原始碼,為了方便查看源碼及調試,把源碼也上傳到 nexus 倉庫中task sourcesJar(type: Jar) {  classifier = 'sources'  from sourceSets.main.allSource}// 打 javadoc 包,為了方便查看注釋,需要把 javadoc 也上傳到 nexus 倉庫中task javadocJar(type: Jar, dependsOn: javadoc) {  classifier = 'javadoc'  from javadoc.destinationDir}artifacts {  archives jar  archives sourcesJar  archives javadocJar}uploadArchives {    repositories {        mavenDeployer {            snapshotRepository(url: "${REPOSITORY_HOME}/nexus/content/repositories/snapshots/") {                authentication(userName: 'xxx', password: 'xxx')            }            repository(url: "${REPOSITORY_HOME}/nexus/content/repositories/releases/") {                authentication(userName: 'xxx', password: 'xxx')            }        }    }}version = '0.0.1' // 設置版本group = 'com.suixingpay.demo' // 設置 group iddescription = 'demo' // 設置描述dependencies {    compile('org.springframework.boot:spring-boot-starter-actuator')    compile('org.springframework.boot:spring-boot-starter-web')    compileOnly('org.springframework.boot:spring-boot-configuration-processor')    compileOnly('org.projectlombok:lombok')    testCompile('org.projectlombok:lombok')    testCompile "org.springframework.boot:spring-boot-starter-test"}

通過上面代碼我們可以看出以下問題:

雖然 dependencies 部分代碼比 maven 少了許多,但總體來看,代碼並不簡潔;

存在硬編碼帶來的風險,比如要修改 Maven 倉庫的地址,或用戶名及密碼時,需要通知所有人進行變更;

gradle 代碼除 buildscript 代碼塊外,沒有像 maven 中 xml 一樣結構性書寫要求,那麼可能造成每個人的寫法是不一樣的情況;

很難區分每個插件的配置都有哪些,對於不熟悉插件的人來說是非常痛苦的事情,非常不方便維護;

當項目多時,會有非常多的重複代碼,增加了非常多的重複開發及維護成本;比如之前沒有要求將源碼及 javadoc 上傳到 maven 倉庫中,後來因各種原因,增加了這項需求,那麼就需要通知所有把所有代碼都修改一遍;

我們最初的想法是將上面代碼,根據不同的插件,使用不同的 gradle 文件來維護,再將它們放到 GRADLE_HOME/init.d/ 目錄下,但還是更新難的問題。

補充說明 init.gradle 的加載順序,Gradle 會依次對一些目錄進行檢測,按照優先級加載這些目錄下的文件,如果一個目錄下有多個文件被找到,則按照英文字母的順序依次加載。加載優先級如下:

通過 -I 或者 –init-script 參數在構建開始時指定路徑,如

gradle --init-script init.gradle clean gradle --I init.gradle assembleDebug

加載 USER_HOME/.gradle/init.gradle 文件

加載 USER_HOME/.gradle/init.d/ 目錄下的以.gradle 結尾的文件

加載 GRADLE_HOME/init.d/ 目錄下的以.gradle 結尾的文件

後來研究發現 gradle 可以通過 apply from 命令加載外部 gradle 文件,甚至可以加載遠程 http 伺服器中的文件。這個發現讓我們興奮不已,有了它可以幫助我們解決上面所有的問題。

首先將上面 build.gradle 的內容拆分成多個文件:

將 maven 相關的代碼放入 maven.gradle 文件中;

因 java、spring boot 和 Spring Cloud 是我們平時用得最多的插件,所以我們將它們都相關的代碼放到了同一個文件中:spring-cloud.gradle,但 Spring boot 及 Spring Cloud 的版本會因為項目的不同而使用不同的版本,所以需要將它們的單獨提取出來,比如:spring-cloud-dalston-sr4.gradle、spring-cloud-edgware.gradle

然後將上面拆分好的文件入到 git 倉庫中,並修改 build.gradle 文件:

buildscript { // buildscript 不能抽取出來,只能重複寫。  ext{    sxGradleHome = "https://raw.githubusercontent.com/sxfad/gradle-scripts/master/"  }  apply from: sxGradleHome + 'maven.gradle'  apply from: sxGradleHome + 'spring-cloud-edgware.gradle' // 導入使用 Spring Cloud 及相應的 Spring Boot 版本號  repositories {    maven { url REPOSITORY_URL }  }  dependencies {    classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")  }}// 參考 https://github.com/sxfad/gradle-scriptsapply from: sxGradleHome + 'maven.gradle'apply from: sxGradleHome + 'spring-cloud.gradle'version = '0.0.1' // 設置版本group = 'com.suixingpay.demo' // 設置 group iddescription = 'demo' // 設置描述dependencies {    compile('org.springframework.boot:spring-boot-starter-actuator')    compile('org.springframework.boot:spring-boot-starter-web')    compileOnly('org.springframework.boot:spring-boot-configuration-processor')    compileOnly('org.projectlombok:lombok')    testCompile('org.projectlombok:lombok')    testCompile "org.springframework.boot:spring-boot-starter-test"}

通過上面的方法處理後,給我們帶來如下好處:

修改後的 build.gradle 變得極其簡潔,只需要關心其基本信息及依賴即可;

對於公司內部使用來說,讓 gradle 的使用更加規範和標準,也使用變得更加簡單;

因為核心 gradle 文件放到了遠程伺服器中,非常方便更新和維護;並且使用 git 管理後,還帶有「版本管理」功能(方便查看文件修改歷史,及回退等);

看到這之後,你一定不會再為選擇 Maven 還是 Gradle 糾結,甚至已經愛上了 Gradle。

作者簡介

邱家榆,隨行付架構師,專注於分布式系統架構及微服務;AutoLoadCache 開源框架作者。

活動推薦

8 月 18 日,InfoQ 將舉辦一場面向技術人的區塊鏈大會!超過二十個區塊鏈落地案例,區塊鏈前沿技術剖析,區塊鏈生態、服務盤點和解讀,盡在 BCCon2018!點擊查看原文進入大會官網了解更多信息。

相關焦點

  • 架構設計-從零開始搭建gradle多模塊項目
    gradle 簡介為什麼選gradle沒有用maven呢 ,gradle做為新一代的構建工具,說實話,構建速度,我還真沒對比過,但就方便省事而言,gradle還是挺省事的,寫的代碼變少了,也清晰了不少,而且迭代升級超快,去年第一次接觸gradle的時候還是4.x的版本,現在都5.x了gradle 配置多模塊項目想要學習gradle多模塊最好的方式,就是看看spring源碼
  • 3種方法帶你玩自定義Android Gradle插件
    根據官網描述,自定義插件一共有三種方式:https://docs.gradle.org/current/userguide/custom_plugins.htmlBuild script——gradle文件,構建腳本內,不過這種只能在文件內使用buildSrc project——buildSrc工程,可以在自己的工程內隨意使用,但是其他工程就無法使用
  • 擁抱Android Studio(二):Android Studio與Gradle深入
    /gradlew -p app buildSync正常情況下,修改了 build.gradle 文件,文件上方就會有一個 sync 的按鈕,點擊之後會重新構建整個 build.gradle。但是某些特殊情況,這個同步可能會失敗。那就需要一個額外的觸發。
  • javaFX(二)-使用gradle+jdk14創建javafx程序
    在本篇文章中,我們一起來學習gradle來新建一個javafx的Hello World 程序.本文中這段最重要這段準備講一下, 為什麼要學習javafx, 小刀學這個, 是出於自己的興趣,當時是公司用的k8s,但是想看日誌啥的,還要先打開網頁,登錄阿里雲,然後選對應的集群,然後找到對應的命名空間,然後找到對應的容器組,然後再點日誌.
  • Gradle 與 Android 構建入門
    大家也許有偶爾會想,為什麼需要 Gradle 這個自動化構建工具?默認創建的 Android 工程裡的文件都有什麼含義?什麼是依賴管理?Android 的打包流程是什麼?通過閱讀本文,可以大致了解 Gradle 是如何工作,可以有針對性搜索相關內容,更加快速的解決常見編譯錯誤。一 為什麼需要自動化構建工具?
  • 寫給Android開發的Gradle知識體系
    在 Android的官方網站提到了新的Android構建系統主要有以下幾個特點:Gradle的Android插件結合Android Studio成為了目前最為流行的Android構建系統。2.這篇文章主要介紹項目build.gradle和模塊build.gradle。
  • 從停用Maven,擁抱Gradle開始,學習SpringBoot
    前言SpringBoot2.3一系列版本發布以來,有了很多的特性,比如優雅的關機。
  • Gradle 創建java項目詳細步驟
    Java構建工具三強: Ant, Maven, GradleAnt歷史悠久, 用build.xml 描述, 當時他的xml著實讓很多工程師頭痛, 但仍有用武之地. Maven 用pom.xml 文件描述, 是對ant的補充, 項目統一管理的得力助手, 統一的依賴包使得項目不再為版本不一致而發愁.
  • 這一次,徹底了解 Gradle 吧!
    Groovy熟悉 Groovy 語法熟悉什麼是 plugin 插件、task 任務熟悉 Gradle 核心對象:Gradle、Setting、Project,Gradle 構建流程、生命周期、及其 hook 勾子函數熟悉 Android 項目構建,application 這個插件了解自定義 task,自定義 plugin 並上傳 maven
  • Maven入門到精通
    Maven有三套相互獨立的生命周期我們在idea創建maven項目之後,有一個非常重要的文件pom.xml,大家可以打開看一下,如下:<?xml version="1.0" encoding="UTF-8"?
  • 給 Android 開發者的 Gradle 入門指南
    「有什麼需要這麼久?」我會問 Android Studio ,希望有某種跡象表明它不僅僅是卡住了。在工作中,每當我看到一個 Android 開發者茫然地盯著屏幕,我經常開玩笑地問他們:「你是在等 Gradle 構建嗎?最終我感到沮喪,並決定找出要如何減少構建時間。
  • Gradle敏捷打包,多版本,多渠道,多環境,多功能,多模塊隨心所欲
    所以果斷拒絕了,加之自己也沒時間去錄視頻,也不想參與什麼商業化活動,只是安靜的做一個開發者,自我定位一下,雖然不能像老羅一樣出身入化,也不能像極客一樣開源萬篇,更不想隨天天發個入門的demo,就想有時間就分享下自己了解的東西,就這麼簡單!
  • 這一次,徹底了解 Gradle 吧!
    Groovy熟悉 Groovy 語法熟悉什麼是 plugin 插件、task 任務熟悉 Gradle 核心對象:Gradle、Setting、Project,Gradle 構建流程、生命周期、及其 hook 勾子函數熟悉 Android 項目構建,application 這個插件了解自定義 task,自定義 plugin 並上傳 maven
  • 掌控 Android Gradle
    例如我們新建一個 Android 工程,在其根目錄中輸入:gradle tasks -q可以看到如下輸出(你可能需要事先配置gradle的環境變量,或也可使用./gradlew替代):根據上圖可以看到當前工程中的每條task都已羅列出,並且有黃色的輸出表示當前task的描述。