轉自:NO END FOR LEARNING http://benweizhu.github.io/blog/2015/01/31/deep-into-gradle-in-action-1/
什么是構建工具?
一個可編程的工具,能夠以可執行和有序的任務來表達滿足需要的自動化過程。
以Java為例,要得到一個簡單可運行的Jar文件,需要下面幾步:
1.編譯源代碼
2.運行測試(前提是你有測試) 3.拷貝Class文件到目標目錄
4.打包Class文件為Jar文件
這是一個完整的可自動化的過程,在沒有構建工具之前,是由誰來做?IDE。一個強大的IDE,以上的步驟都只需要按幾個按鈕,這讓開發人員的生活變得很美好,完全集中在寫出優秀的代碼。
現在,本來整個開發過程只需要你一個人,隨着任務的難度和復雜度的加劇,你的團隊從一個人變成了3個人或者更多。這時,你肯定會需要代碼集成,這個問題好解決,使用版本控制,無論是中心式的SVN還是分布式的Git,總之可以既可以解決版本問題,也解決代碼集成的問題。
這種情況下,在沒有自動化構建時,你肯定會遇到下面幾個問題:
1.在我的機器上可以跑
2.從版本控制check out代碼,發現編譯不過,有人少提交了代碼文件
3.有個人提交代碼時沒跑測試,導致其他人check out代碼后,測試跑不過
4.版本發布時,由一個人來check out所有代碼,在他自己的機器上編譯打包,結果部署到服務器上運行不了
…
導致上面這些問題出現的原因都有兩個特點:
1.手動介入
2.重復任務
開發人員要關注的應該是編寫實現功能的代碼,至於編譯編譯代碼,拷貝文件,運行測試等一切重復和可自動化的事情都應該交給機器去做,因為人是容易犯錯的。
Java世界的構建工具
在Java的世界里,目前在被使用的常用構建工具有三個:Ant,Maven,Gradle。
Ant的核心是由Java編寫,采用XML作為構建腳本,這樣就允許你在任何環境下,運行構建。Ant基於任務鏈思想,任務之間定義依賴,形成先后順序。缺點是使用XML定義構建腳本,導致腳本臃腫,Ant自身沒有為項目構建提供指導,導致每個build腳本都不一樣,開發人員對於每個項目都需要去熟悉腳本內容,沒有提供在Ant生態環境內的依賴管理工具。
Maven團隊意識到Ant的缺陷,采用標准的項目布局,和統一的生命周期,采用約定由於配置的思想,減少構建腳本需要的編寫內容,活躍的社區,可以方便找到合適的插件,強大的依賴管理工具。缺點是采用默認的結構和生命周期,太過限制,編寫插件擴展麻煩,XML作為構建腳本。
如果有一個構建工具可以折中,同時擁有Ant和Maven的優點,是不是很爽?告訴你有,那就是Gradle。
Gradle
基於Groovy的DSL,提供聲明式的構建語言
采用標准的項目布局,但擁有完全的可配置性,就是可以改
通過插件,提供默認的構建生命周期,也可以自己定義任務,單獨運行任務,定義任務間的依賴
強大的依賴管理工具,與Maven和Ivy倉庫結合
與Ant天生兼容,有效的重用Ant的任務
多種實現插件的方式,強大的官方插件庫
從構建級別,支持從Ant或者Maven的逐步遷移
通過包裝器,無縫的在各個平台運行
看一個超級簡單的例子:
如果你的項目采用標准的Maven布局(Java世界的標准布局)
1
2 3 4 5 6 7 8 9 10 |
|
在項目根目錄下,創建一個build.gradle,這個是Gradle的構建腳本文件,就和build.xml,POM.xml道理一樣。
那么你要實現Java的編譯,測試,拷貝class到目標目錄,打包Jar文件等,只需要在構建腳本中,使用下面一句話,使用Java插件。
1
|
|
然后運行gradle build。
下一篇,我們深入到實戰學習Java插件的使用和依賴管理,讓你快速開始Java應用開發。
沒有介紹Gradle的基礎知識,直接開始實戰,目的是為了更快的讓大家開始使用Gradle做構建,快速上手,當需要實現的自動化需求更復雜時,在深入學習基礎知識。
這一篇,我們直接開始Java插件的使用。
應用Java插件
Gradle是一個通用構建工具,也就是說,它不單是為Java而生。比如,還可以做Groovy,Scala的構建。這取決於你使用什么樣的插件。
大部分Java項目的基本步驟都非常類似,編譯Java源代碼,運行單元測試,拷貝生成的class文件到目標目錄,打包Jar文件(或者War包,Ear包),而這些重復且約定俗成的任務,如果可以不用寫一行構建代碼就實現,是多么的棒!Maven就做到這一點,采用約定由於配置的思想,預先定義常用的任務,並定義它們的執行順序。
Gradle吸收了Maven的這個優點,通過插件,實現預定義任務和任務之間依賴關系的導入,這樣就可以在一行代碼都不寫的情況下(如果應用插件,你覺得也算一行的話,那就寫一行吧),直接使用已經定義的任務。
1
|
|
SourceSet和項目布局
就和Maven一樣,在默認的情況下,項目的目錄結構是固定的Java世界的標准項目目錄布局,只不過Maven的不可以改,但是Gradle可以改。
1
2 3 4 5 6 7 8 9 10 |
|
Java插件引入了一個概念叫做SourceSets,它代表了一組源文件,通過修改SourceSets中的屬性,可以指定哪些源文件(或文件夾下的源文件)要被編譯,哪些源文件要被排除。Gradle就是通過它實現Java項目的布局定義。
Java插件默認實現了兩個SourceSet,main和test。每個SourceSet都提供了一系列的屬性,通過這些屬性,可以定義該SourceSet所包含的源文件。比如,java.srcDirs,resources.srcDirs。Java插件中定義的其他任務,就根據main和test的這兩個SourceSet的定義來尋找產品代碼和測試代碼等。
在構建腳本中,怎么樣定義或者修改SourceSet呢?Gradle提供了一系列的DSL,可以讓你方便的定義或者修改配置。比如,sourceSets的DSL。
1
2 3 4 5 6 7 8 9 10 |
|
上面的這個例子,在sourceSets中,修改了Java插件中已經定義的SourceSet main,修改了它的java.srcDir和resources.srcDir。於是,項目的目錄結構就改變了。
改變Java插件中預定義的項目目錄結構,不是我們最終的目的,因為它是目前Java世界,標准的項目布局,或者說大家都遵守的項目布局。
sourceSets最主要的作用是增加新的目錄約定,比如,你想要定義一個新的SourceSet來管理集成測試的源文件,這樣可以將單元測試和集成測試分開管理。
至於,關於具體如何為集成測試寫一個新的SourceSet會在后面介紹依賴管理時舉例說明。
Java插件提供的任務
Java插件提供了一系列的任務給你使用,包括編譯,運行測試,打包等等。當你在項目中應用Java插件時,就已經將這些任務集成到你的項目中了。
在命令行中,運行gradle tasks命令,可以查看當前項目下主要的task。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
你可以對比Java插件應用前和應用后該命令的輸出,Java插件提供的任務有很多,至於每個任務是做什么,這里就不贅述了。
Java插件除了為你預定義這些任務,該預定義了這些任務之間的依賴關系。如下圖:

你也可以通過命令gradle tasks –all來查看每個task各自有什么依賴。
當然,這里還是重點提下,Java插件中四個重要和常用的任務,assemble,check,build,clean。
assemble
All archive tasks in the project, including jar. Some plugins add additional archive tasks to the project.
check
All verification tasks in the project, including test. Some plugins add additional verification tasks to the project.
build
check and assemble
clean
Deletes the project build directory.
assemble被用來產生Jar文件,輸出目錄在build/libs下。
check用來運行所有的驗收任務,包括test任務,以及其他驗收任務,比如checkstyle。
Tips:在命令行中運行單個測試
JAVA插件中的test任務提供了一個filter屬性,可以幫助指定運行test任務時,什么測試源文件要包含,什么要排除。
1
2 3 4 5 6 7 8 9 10 11 12 |
|
當然一般情況下,你不會這么去做。
但重點是,你可以通過命令行傳遞的參數來指定這個matching規則,這樣你就可以通過命令行來指定跑某一類測試,或者單個測試。你一定遇到過,某個測試在命令行中可以運行,在IDE中不能運行,或者反過來。這時,你可以不會想要跑全部的測試來驗證某一個測試。於是,你就可以通過命令行來運行某一個測試:
1
2 |
|
到目前為止,你已經了解了Java插件提供的一些核心功能和有用小技巧。雖然還未涉及到Jar任務和uploadfile任務(這些任務當需要時,再去看就行了),但是就啟動項目而言,對Java插件的使用所需要了解的知識已經足夠了。
下一節,講解依賴管理
大部分的項目都不是自包含的,也就是說,需要使用到其他項目的構建結果,比如一些Jar文件。它們作為輸入文件,必須存在於項目的ClassPath下,程序才能編譯和運行。這些輸入文件有一個很表意的名字,叫做依賴。
Gradle允許你告訴它項目的依賴是什么,然后它就會負責找到這些依賴。這些依賴會從Maven或者Ivy的遠程倉庫下載下來(大部分情況),並緩存在本地的某個路徑,這個過程叫做依賴解析。
Maven和Gradle一樣也提供了類似的功能,而Ant沒有,你只能告訴Ant依賴文件的相對或者絕對路徑,讓它去加載。
常常一個依賴自己也存在依賴,我們稱為傳遞依賴,依賴管理工具又具有解析傳遞依賴的能力。
Gradle的依賴管理
那么如何在Gradle中定義依賴呢?看個最簡單的例子。
1
2 3 4 5 6 7 8 9 |
|
項目使用了Java的插件,在repositories塊中告訴Gradle使用maven的遠程倉庫作為依賴下載地址,在dependencies塊定義了一個junit的依賴,並說明了分組(Maven中的Scope),后面注釋中有一個表意更完整的依賴定義,說明了依賴聲明使用的三個坐標group,name,version。
整個看起來是那么的表意,使用過Maven更會覺得是無縫轉換,甚至更簡潔。
Dependency configurations 依賴分組
在Gradle中,依賴都被會分配到某一個具體的configuration中(這里我不傾向於翻譯成配置,我覺得布局,或者分組更適合)。Configuration代表着一個或多個構件及構件所需依賴的一個分組。
Java插件已經預定義了一些configuration,比如,compile,runtime,testCompile,testRuntime等。
compile 放在這個configuration下的依賴是在編譯產品代碼時所使用的,但它作為一個分組,包含產品代碼和編譯所需的依賴。
runtime 產品代碼在運行時需要的依賴,默認,也會包含compile中的依賴。
testCompile 編譯測試代碼時所需要的依賴,默認,被編譯的產品代碼和產品代碼需要的編譯依賴也屬於該分組。
testRuntime 運行測試時需要的依賴。默認,包含compile,runtime和testCompile的分組的構建和依賴。
使用過Maven的都應該知道分組的含義,這里講解給不明白的同學,依賴之所以要分組,是因為,每個階段對依賴的需要不一樣,最明顯的是產品代碼和測試代碼,比如junit在產品代碼中就不需要。
那么,為什么產品代碼的編譯階段和運行階段也分組,一般編譯階段需要的依賴,在運行階段也需要,但是反過來就不一定了。比如,你通過反射去load一個class,這時該class就不一定需要在編譯階段存在。
一個更常見的例子,做web開發時需要servlet的依賴,但是只是編譯階段,運行時servlet依賴由servlet容器來提供。所以Gradle的War插件也提供了兩個configuration,分別是providedCompile和providedRuntime,它們對依賴的使用范圍定義和compile以及runtime一致,只不過依賴的Jar包不會被加到War包里面。
定義SourceSet時,添加的Configuration
上一節,在介紹Java插件的時候,提到了SourceSet概念。針對每一個新添加的SourceSet,Java插件都會動態的給它添加兩個Configuration,分別是sourceSetCompile和sourceSetRuntime。
比如:新添加一個SourceSet,叫做int,那么對應的Configuration是intCompile和intRuntime。
這一特性也正好印證,Java插件是如何識別自定義SourceSet來進行編譯和運行。
依賴的多種定義方式
除了通過遠程倉庫和依賴坐標來定義依賴,Gradle還提供了另外兩種常用的依賴定義方式,對本地文件的依賴,對某個項目的依賴。
對文件的依賴
這種情況看起來是不是很奇葩,都有依賴管理了和Maven倉庫了還要什么文件依賴。其實不然,使用這種定義方式,最常見場景是項目構建工具的遷移,從Ant到Gradle。無論任何項目,遷移過程都是小步前進,Gradle提供文件依賴的配置,就是為了解決這些特殊性。
1
2 3 4 |
|
對另一個工程的依賴
項目中划分子模塊是很平常的事情,前端Controller和數據層Dao分離管理就是一個例子,那么在進行前端Controller模塊構建時,就需要將數據層模塊作為依賴。定義方式如下:
1
2 3 |
|
依賴版本沖突
依賴沖突是所以依賴管理中最頭痛的問題,這常常出現在傳遞依賴中。Gradle對解決傳遞依賴提供了兩種策略,使用最新版本或者直接導致構建失敗。默認的策略是使用最新版本。雖然這樣的策略能夠解決一些問題,但是還是不夠。常見的一種情況是,NoSuchMethond或者ClassNotFound。這時候,你可能需要一些特殊手段,比如排除不想要的傳遞依賴。
排除傳遞依賴
排除傳遞依賴有多種原因,遠程倉庫中不存在,運行時不需要,或者版本沖突。排除傳遞依賴的方式有兩種:1.直接在configuration中排除 2.在具體的某個dependency中排除
1
2 3 4 5 6 7 8 9 10 |
|
通過命令行查看依賴關系
當出現依賴沖突時,最主要的還是要分析依賴沖突的原因,Gradle提供了兩個任務來幫助你分析依賴關系
dependencies - Displays all dependencies declared in root project ‘projectReports’.
dependencyInsight - Displays the insight into a specific dependency in root project ‘projectReports’.
Tips:輸出依賴關系圖到文件
在命令行中直接使用gradle dependencies可以打印出依賴圖,但是在命令行中查看始終不太方便,我們可以將結果輸出到一個文件中,如下:
1
|
|
dependencies.txt保存在項目的根目錄
Gradle的官方文檔中關於Gradle的依賴管理的內容還有很多,比如,如何訪問需要用戶名密碼授權的Maven倉庫等等。等多內容,可以參考官方文檔:http://gradle.org/docs/current/userguide/dependency_management.html
下一節,利用前三節學到的知識,編寫集成測試任務,並單獨划分SourceSet。
