有了Gradle,還會選Maven嗎?
現在許多人還在為使用Maven 還是 Gradle 而糾結。如果關注過《Maven權威指南》作者許曉斌老師在InfoQ中發表的文章:ofollow,noindex" target="_blank">《Maven實戰(六)——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_8 targetCompatibility = JavaVersion.VERSION_1_8 bootRepackage { // 預設只打普通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 id description = '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://gitee.com/sxfad/gradle-scripts/raw/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://gitee.com/sxfad/gradle-scripts/ apply from: sxGradleHome + 'maven.gradle' apply from: sxGradleHome + 'spring-cloud.gradle' version = '0.0.1' // 設定版本 group = 'com.suixingpay.demo' // 設定group id description = '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管理後,還帶有“版本管理”功能(方便檢視檔案修改歷史,及回退等);
本文來自雲棲社群合作伙伴“開源中國”
本文作者:qiujiayu