導語:
隨著技術的發(fā)展,不管是前端開發(fā)蹲堂、服務端開發(fā)或者是移動端開發(fā)(移動也是前端的一個分支)中都會用到自動化構建工具掌猛。如果我們沒有使用過自動化構建工具,可能對自動構建工具沒有什么概念佩研,為什么需要用到自動化構建工具。我們先來看一下在沒有使用自動化構建工具前霞揉,我們是如何開發(fā)項目旬薯、管理項目的。項目開發(fā)過程中涉及新建項目-》代碼開發(fā)—》依賴管理-》編譯-》測試-》打包-》上傳等過程适秩。依賴管理绊序,項目開發(fā)過程中會用到很多jar包,自己開發(fā)的有別人開發(fā)的秽荞,這些都放在項目的lib目錄骤公。大點的項目幾百個,很容易造成依賴沖突蚂会,另外就是版本更新特別不方便淋样,你得手動去復制新版本的jar包放到lib目錄下耗式。測試過程胁住,大家都不重視趁猴,基本上寫一個java類,在main方法調用調試幾下就完了彪见。打包儡司,得手動,如在Eclipse上導出jar包余指,web開發(fā)的話導出war包捕犬。另外發(fā)布不同版本,還得手動去更改后才能打出自己想要的酵镜。上傳過程中碉碉,得手動或用ftp上傳到服務器上面。這些過程太頻繁淮韭、瑣碎無聊垢粮,小點項目還能管理的過來,大的項目簡直是災難靠粪。技術大牛們忍無可忍總于爆發(fā)了蜡吧,發(fā)明出了構建工具這個東西,將程序員們從水深火熱中解脫出來占键。
構建工具的作用
- 依賴管理
- 自動測試昔善、自動打包、自動發(fā)布到制定的地方去
- 機器能干的畔乙,我們絕不自己動手
Java方面主流的構建工具
- 最早出現(xiàn)的Ant君仆,提供了編譯、測試啸澡、打包三個最基本的功能
- 接著Maven,在Ant的基礎上加了依賴管理和發(fā)布功能袖订,通過xml標記性語言來進行管理構建腳步
- 接著重點來了,我們要學習的Gradle嗅虏,它在Maven基礎上更近一步洛姑,使用Groovy進行管理構建腳步,不再使用xml 皮服,因為項目大了之后使用xml很容易造成啰唆楞艾、臃腫難以管理。而使用這種特定領域語言來管理構建腳步具有更高的可拓展性和靈活性龄广。
Gradle是什么
一個開源的項目自動化構建工具硫眯,建立在Apache Ant 和 Apache Maven概念的基礎上,并引入了基于Groovy的特定領域語言(DSL)择同,而不再使用xml形式的管理構建腳本两入。
Gradle的作用,即能為我們做什么
Gradle是一個項目自動化構建工具敲才,它當然具備自動化構建的所有功能裹纳,依賴管理择葡,自動化測試、自動化打包剃氧,發(fā)布到制定的地方去敏储。另外它具有很高的拓展性和靈活性,你想要它做什么朋鞍,它都能幫你完成已添。
準備使用Gradle
Gradle的安裝、配置環(huán)境變量
??1. 因為Gradle是基于JVM的所以確保本地安裝了JDK java -version
??2. 打開官網下載安裝包滥酥,解壓到制定的目錄Gradle官網https://gradle.org/docs#getting-started
??3. 配置環(huán)境變量更舞,GRADLE_HOME添加到path中,%GRADLE_HOME%\bin;
??4. 驗證是否安裝成功坎吻,gradle -v
groovy語言基礎知識
Groovy是什么
??Groovy是用Java虛擬機的一種敏捷的動態(tài)語言疏哗,它是一種成熟的面向對象編程語言,即可用于面向對象編程禾怠,又可以用作純粹的腳本語言返奉。使用該語言不需要編寫太多的代碼,同時又具有閉包和動態(tài)語言中的其他特性吗氏。
下面的知識點都可以到官網文檔中查詢到http://www.groovy-lang.org/documentation.html
??與java相比
??1芽偏、Groovy完全兼容java語法,最終也編譯成字節(jié)碼
??2弦讽、Groovy注釋標記和Java一樣污尉,支持//或者//
??3、Groovy中支持動態(tài)類型往产,即定義變量的時候可以不指定其類型被碗。
??4、Groovy中仿村,變量定義可以使用關鍵字def锐朴。注意,雖然def不是必須的蔼囊,但是為了代碼清晰焚志,建議還是使用def關鍵字。
def var =1
def str= "i am a person"
def int x = 1//也可以指定類型
5畏鼓、函數(shù)定義時酱酬,參數(shù)的類型也可以不指定。比如
String function(arg1,args2){//無需指定參數(shù)類型
}
6云矫、除了變量定義可以不指定類型外膳沽,Groovy中函數(shù)的返回值也可以是無類型的。
比如://無類型的函數(shù)定義,必須使用def關鍵字
def nonReturnTypeFunc(){
last_line //最后一行代碼的執(zhí)行結果就是本函數(shù)的返回值
}
//如果指定了函數(shù)返回類型挑社,則可不必加def關鍵字來定義函數(shù)
String getString(){
return "I am a string"
}
其實呵俏,所謂的無返回類型的函數(shù),我估計內部都是按返回Object類型來處理的滔灶。畢竟,Groovy是基于Java的吼肥,而且最終會轉成Java Code運行在JVM上录平。
??7、 函數(shù)返回值:Groovy的函數(shù)里缀皱,可以不使用return xxx來設置xxx為函數(shù)返回值斗这。如果不使用return語句的話,則函數(shù)里最后一句代碼的執(zhí)行結果被設置成返回值啤斗。比如
//下面這個函數(shù)的返回值是字符串"getSomething return value"
def getSomething(){
"getSomething return value" //如果這是最后一行代碼表箭,則返回類型為String
1000 //如果這是最后一行代碼,則返回類型為Integer
}
注意钮莲,如果函數(shù)定義時候指明了返回值類型的話免钻,函數(shù)中則必須返回正確的數(shù)據(jù)類型,否則運行時報錯。如果使用了動態(tài)類型的話崔拥,你就可以返回任何類型了极舔。
??8、Groovy對字符串支持相當強大链瓦,充分吸收了一些腳本語言的優(yōu)點:單引號''中的內容嚴格對應Java中的String拆魏,不對$符號進行轉義
def singleQuote='I am $ dolloar' //輸出就是I am $ dolloar
雙引號""的內容則和腳本語言的處理有點像,如果字符中有$號的話慈俯,則它會$表達式先求值渤刃。
def doubleQuoteWithoutDollar = "I am one dollar" //輸出 I am one dollar
def x = 1
def doubleQuoteWithDollar = "I am $x dolloar" //輸出I am 1 dolloar
三個引號'''xxx'''中的字符串支持隨意換行 比如
def multieLines = ''' begin
line 1
line 2
end '''
9、 Groovy語句可以不用分號結尾贴膘。Groovy為了盡量減少代碼的輸入卖子,確實煞費苦心
??10、除了每行代碼不用加分號外刑峡,Groovy中函數(shù)調用的時候還可以不加括號揪胃。比如:
println("test") ---> println "test"
注意,雖然寫代碼的時候氛琢,對于函數(shù)調用可以不帶括號喊递,但是Groovy經常把屬性和函數(shù)調用混淆。比如
def getSomething(){
"hello"
}
getSomething() //如果不加括號的話阳似,Groovy會誤認為getSomething是一個變量骚勘。
??數(shù)據(jù)類型的不同
?? Groovy中的數(shù)據(jù)類型我們就介紹兩種和Java不太一樣的:
?? 一個是Java中的基本數(shù)據(jù)類型。
?? 另外一個是Groovy中的容器類。
?? 最后一個非常重要的是閉包俏讹。
?? 基本數(shù)據(jù)類型
?? 作為動態(tài)語言当宴,Groovy世界中的所有事物都是對象。所以泽疆,int枫绅,boolean這些Java中的基本數(shù)據(jù)類型,在Groovy代碼中其實對應的是它們的包裝數(shù)據(jù)類型羽圃。比如int對應為Integer牛曹,boolean對應為Boolean。比如下圖中的代碼執(zhí)行結果:
?? 容器類
?? List類其實是ArrayList類
變量定義:List變量由[]定義瓢娜,比如
def aList = [5,'string',true] //List由[]定義挂洛,其元素可以是任何對象
變量存取:可以直接通過索引存取眠砾,而且不用擔心索引越界虏劲。如果索引超過當前鏈表長度,List會自動
往該索引添加元素
assert aList[1] == 'string'
assert aList[5] == null //第6個元素為空
aList[100] = 100 //設置第101個元素的值為10
assert aList[100] == 100
那么褒颈,aList到現(xiàn)在為止有多少個元素呢柒巫?
println aList.size ===>結果是101
Map類其實是LinkedHashMap類
容器變量定義
變量定義:Map變量由[:]定義,比如
def aMap = ['key1':'value1','key2':true]
Map由[:]定義谷丸,注意其中的冒號吻育。冒號左邊是key,右邊是Value淤井。key必須是字符串布疼,value可以是任何對象。另外币狠,key可以用''或""包起來游两,也可以不用引號包起來。比如
def aNewMap = [key1:"value",key2:true] //其中的key1和key2默認被
處理成字符串"key1"和"key2"
不過Key要是不使用引號包起來的話漩绵,也會帶來一定混淆贱案,比如
def key1="wowo"
def aConfusedMap=[key1:"who am i?"]
aConfuseMap中的key1到底是"key1"還是變量key1的值“wowo”?顯然止吐,答案是字符串"key1"宝踪。如果要是"wowo"的話,則aConfusedMap的定義必須設置成:
def aConfusedMap=[(key1):"who am i?"]
Map中元素的存取更加方便碍扔,它支持多種方法:
println aMap.keyName <==這種表達方法好像key就是aMap的一個成員變量一樣
println aMap['keyName'] <==這種表達方法更傳統(tǒng)一點
aMap.anotherkey = "i am map" <==為map添加新元素
Range類
??Range是Groovy對List的一種拓展瘩燥,變量定義和大體的使用方法如下:
def aRange = 1..5 <==Range類型的變量 由begin值+兩個點+end值表示
左邊這個aRange包含1,2,3,4,5這5個值
如果不想包含最后一個元素,則
def aRangeWithoutEnd = 1..<5 <==包含1,2,3,4這4個元素
println aRange.from
println aRange.to
高級特性閉包
??閉包不同,英文叫Closure厉膀,是Groovy中非常重要的一個數(shù)據(jù)類型或者說一種概念了溶耘。閉包的歷史來源,種種好處我就不說了服鹅。我們直接看怎么使用它凳兵!閉包,是一種數(shù)據(jù)類型企软,它代表了一段可執(zhí)行的代碼庐扫。其外形如下:
def aClosure = {//閉包是一段代碼,所以需要用花括號括起來..
String param1, int param2 -> //這個箭頭很關鍵仗哨。箭頭前面是參數(shù)定義形庭,箭頭后面是代碼
println"this is code" //這是代碼,最后一句是返回值藻治,
//也可以使用return,和Groovy中普通函數(shù)一樣
}
簡而言之巷挥,Closure的定義格式是:
def xxx = {paramters -> code} //或者
def xxx = {無參數(shù)桩卵,純code} 這種case不需要->符號
說實話,從C/C++語言的角度看倍宾,閉包和函數(shù)指針很像雏节。閉包定義好后,要調用它的方法就是:閉包對象.call(參數(shù)) 或者更像函數(shù)指針調用的方法:閉包對象(參數(shù))比如
aClosure.call("this is string",100) 或者
aClosure("this is string", 100)
\\上面就是一個閉包的定義和使用高职。在閉包中钩乍,還需要注意一點:
\\如果閉包沒定義參數(shù)的話,則隱含有一個參數(shù)怔锌,這個參數(shù)名字叫it寥粹,和this的作用類似。it代表閉包的參數(shù)埃元。比如:
def greeting = { "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'
\\等同于
def greeting = { it -> "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'
\\但是涝涤,如果在閉包定義時,采用下面這種寫法岛杀,則表示閉包沒有參數(shù)阔拳!
def noParamClosure = { -> true }
\\這個時候,我們就不能給noParamClosure傳參數(shù)了类嗤!
noParamClosure ("test") \\<==報錯喔糊肠!
Closure使用中的注意點
??1、省略圓括號
??閉包在Groovy中大量使用遗锣,比如很多類都定義了一些函數(shù)货裹,這些函數(shù)最后一個參數(shù)都是一個閉包。比如:
public static <T> List<T> each(List<T> self, Closure closure)
上面這個函數(shù)表示針對List的每一個元素都會調用closure做一些處理精偿。這里的closure泪酱,就有點回調函數(shù)的感覺。但是,在使用這個each函數(shù)的時候墓阀,我們傳遞一個怎樣的Closure進去呢毡惜?比如:
def iamList = [1,2,3,4,5] //定義一個List
iamList.each{ //調用它的each,這段代碼的格式看不懂了吧斯撮?each是個函數(shù)经伙,圓括號去哪了?
println it
}
上面代碼有兩個知識點:each函數(shù)調用的圓括號不見了!原來勿锅,Groovy中帕膜,當函數(shù)的最后一個參數(shù)是閉包的話,可以省略圓括號溢十。比如
def testClosure(int a1,String b1, Closure closure){
//do something
closure() //調用閉包}那么調用的時候垮刹,就可以免括號!
testClosure (4, "test", {
println "i am in closure"
} )
//括號可以不寫..
注意张弛,這個特點非常關鍵荒典,因為以后在Gradle中經常會出現(xiàn)圖1這樣的代碼:
??經常碰見圖1這樣的沒有圓括號的代碼。省略圓括號雖然使得代碼簡潔吞鸭,看起來更像腳本語言寺董,但是它這經常會讓我confuse(不知道其他人是否有同感),以doLast為例刻剥,完整的代碼應該按下面這種寫法:
doLast({
println 'Hello world!'
})
有了圓括號遮咖,你會知道 doLast只是把一個Closure對象傳了進去。很明顯造虏,它不代表這段腳本解析到doLast的時候就會調用println 'Hello world!' 御吞。但是把圓括號去掉后,就感覺好像println 'Hello world!'立即就會被調用一樣漓藕!
??2魄藕、如何確定Closure的參數(shù)
??另外一個比較讓人頭疼的地方是,Closure的參數(shù)該怎么搞撵术?還是剛才的each函數(shù):
public static <T> List<T> each(List<T> self, Closure closure)
如何使用它呢背率?比如:
def iamList = [1,2,3,4,5] //定義一個List變量
iamList.each{ //調用它的each函數(shù),只要傳入一個Closure就可以了嫩与。
println it
}
看起來很輕松寝姿,其實:對于each所需要的Closure,它的參數(shù)是什么划滋?有多少個參數(shù)饵筑?返回值是什么?我們能寫成下面這樣嗎处坪?
iamList.each{String name,int x -> return x} //運行的時候肯定報錯根资!
??所以架专,Closure雖然很方便,但是它一定會和使用它的上下文有極強的關聯(lián)玄帕。要不部脚,作為類似回調這樣的東西,我如何知道調用者傳遞什么參數(shù)給Closure呢裤纹?
??此問題如何破解委刘?只能通過查詢API文檔才能了解上下文語義。比如下圖2:
??圖2中:each函數(shù)說明中鹰椒,將給指定的closure傳遞Set中的每一個item锡移。所以,closure的參數(shù)只有一個漆际。findAll中淆珊,絕對抓瞎了。一個是沒說明往Closure里傳什么奸汇。另外沒說明Closure的返回值是什么.....施符。
??對Map的findAll而言,Closure可以有兩個參數(shù)茫蛹。findAll會將Key和Value分別傳進去操刀。并且烁挟,Closure返回true婴洼,表示該元素是自己想要的。返回false表示該元素不是自己要找的撼嗓。示意代碼所示:
def result = aMap.findAll {
key, value ->
println "key=$key,value=$value"
if (key == "k1")
return true
return false
}
Closure的使用有點坑柬采,很大程度上依賴于你對API的熟悉程度,所以最初階段且警,SDK查詢是少不了的粉捻。
??3、腳本類
??groovy也可以像java那樣寫package斑芜,然后寫類
package bean
class Person {
String name
String gender
Person(name, gender) {
this.name = name
this.gender = gender
}
def print() {
println name + " " + gender
}
}
import bean.Person
def name = 'EvilsoulM'
def person=new Person(name,"male");
person.print()
groovy和Java類很相似肩刃。當然,如果不聲明public/private等訪問權限的話杏头,Groovy中類及其變量默認都是public的盈包。
??4、腳本到底是什么
??Java中醇王,我們最熟悉的是類呢燥。但是我們在Java的一個源碼文件中,不能不寫class(interface或者其他....)寓娩,而Groovy可以像寫腳本一樣叛氨,把要做的事情都寫在xxx.groovy中呼渣,而且可以通過groovy xxx.groovy直接執(zhí)行這個腳本。這到底是怎么搞的寞埠?
??Groovy把它轉換成這樣的Java類:執(zhí)行 groovyc -d classes test.groovy groovyc是groovy的編譯命令屁置,-d classes用于將編譯得到的class文件拷貝到classes文件夾下圖4是test.groovy腳本轉換得到的java class。用jd-gui反編譯它的代碼:
??test.groovy被轉換成了一個test類畸裳,它從script派生缰犁。
- 每一個腳本都會生成一個static main函數(shù)。
- 這樣怖糊,當我們groovy test.groovy的時候帅容,其實就是用java去執(zhí)行這個main函數(shù)腳本中的所有代碼都會放到run函數(shù)中。比如伍伤,println 'Groovy world'并徘,這句代碼實際上是包含在run函數(shù)里的。
- 如果腳本中定義了函數(shù)扰魂,則函數(shù)會被定義在test類中麦乞。
groovyc是一個比較好的命令,讀者要掌握它的用法劝评。然后利用jd-gui來查看對應class的Java源碼姐直。
??5、腳本中的變量和作用域
??前面說了蒋畜,xxx.groovy只要不是和Java那樣的class声畏,那么它就是一個腳本。而且腳本的代碼其實都會被放到run函數(shù)中去執(zhí)行姻成。那么插龄,在Groovy的腳本中,很重要的一點就是腳本中定義的變量和它的作用域科展。舉例:
def x = 1 // <==注意均牢,這個x有def(或者指明類型,比如 int x = 1)
def printx(){
println x
}
//printx() <==報錯才睹,說x找不到
為什么徘跪?繼續(xù)來看反編譯后的class文件。
??圖中琅攘,x也沒有被定義成test的成員函數(shù)垮庐,而是在run的執(zhí)行過程中,將x作為一個屬性添加到test實例對象中了乎澄。然后在printx中突硝,先獲取這個屬性。注意置济,Groovy的文檔說 x = 1這種定義將使得x變成test的成員變量解恰,但從反編譯情況看锋八,這是不對的.....(這是infoQ文章中說的,但是測試來說這句話是對的护盈,應該是文章作者沒有定義成class)雖然printx可以訪問x變量了挟纱,但是假如有其他腳本卻無法訪問x變量。因為它不是test的成員變量腐宋。比如紊服,我在測試目錄下創(chuàng)建一個新的名為test1.groovy。這個test1將訪問test.groovy中定義的printx函數(shù):
def atest=new test()
atest.printx()
這種方法使得我們可以將代碼分成模塊來編寫胸竞,比如將公共的功能放到test.groovy中欺嗤,然后使用公共功能的代碼放到test1.groovy中。執(zhí)行groovy test1.groovy卫枝,報錯煎饼。說x找不到。這是因為x是在test的run函數(shù)動態(tài)加進去的校赤。怎么辦吆玖?
import groovy.transform.Field; //必須要先import
@Field x = 1 // <==在x前面加上@Field標注,這樣马篮,x就徹徹底底是test的成員變量了沾乘。
查看編譯后的test.class文件,得到:
??這個時候浑测,test.groovy中的x就成了test類的成員函數(shù)了翅阵。如此,我們可以在script中定義那些需要輸出給外部腳本或類使用的變量了尽爆!
??eg:
??ScriptBase.groovy類 (用了filed 就相當這就是一個class 就不用再自己定義class了)
import groovy.transform.Field;
@Field author = 'EvilsouM'
@Field gender = 'male'
@Field age = 25//必須要先import
def printInfo() {
println "name->$author gender->$gender age->$age"
}
//或者自己定義class
class ScriptBase {
def author = 'EvilsouM'
def gender = 'male'
def age = 25//必須要先import
def printInfo() {
println "name->$author gender->$gender age->$age"
}
}
.groovy類
def Closure printAuthorInfo = {
String name, String gender, int age ->
println "name->$name gender->$gender age->$age"
}
def ScriptBase base = new ScriptBase()
base.printInfo()
printAuthorInfo.call(base.author, base.gender, base.age) //上面兩種方式都能拿到成員變量
文件I/O操作
??本節(jié)介紹下Groovy的文件I/O操作怎顾。直接來看例子吧读慎,雖然比Java看起來簡單漱贱,但要理解起來其實比較難。尤其是當你要自己查SDK并編寫代碼的時候夭委。整體說來幅狮,Groovy的I/O操作是在原有Java I/O操作上進行了更為簡單方便的封裝,并且使用Closure來簡化代碼編寫株灸。主要封裝了如下一些了類:
- 讀文件Groovy中崇摄,文件讀操作簡單到令人發(fā)指:def targetFile = new File(文件名) <==File對象還是要創(chuàng)建的。然后打開http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html看看Groovy定義的API:
- 讀該文件中的每一行:eachLine的唯一參數(shù)是一個Closure慌烧。Closure的參數(shù)是文件每一行的內容其內部實現(xiàn)肯定是Groovy打開這個文件逐抑,然后讀取文件的一行,然后調用Closure...
def File targetFile = new File("build.gradle")
targetFile.eachLine {
String line ->
println line
}
- 直接得到文件內容
targetFile.getBytes() <==文件內容一次性讀出屹蚊,返回類型為byte[]
- 使用InputStream.InputStream的SDK在 http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.html
def ism = targetFile.newInputStream() //操作ism厕氨,最后記得關掉
ism.close
- 使用閉包操作inputStream进每,以后在Gradle里會常看到這種搞法
``
targetFile.withInputStream{
ism -> //操作ism. 不用close命斧。Groovy會自動替你close
}
寫文件和讀文件差不多
??不再啰嗦田晚。這里給個例子,告訴大家如何copy文件国葬。
def srcFile = new File(源文件名)
def targetFile = new File(目標文件名)
targetFile.withOutputStream{
os->
srcFile.withInputStream {
ins->
os << ins //利用OutputStream的<<操作符重載贤徒,完成從inputstream到OutputStream //的輸出
}
}
以上的知識點都可以去官網查看API,Groovy的API文檔位于 http://www.groovy-lang.org/api.html
第一個Gradle項目汇四,領略Gradle的風采
Gradle構建腳步基本原理部分
構建腳步介紹
??Gradle構建中的兩個基本概念就是項目(project)和任務(task),每個構建(build.gradle)至少包含一個項目接奈,項目中包含一個或多個任務。在多項目構建中通孽,一個項目可以依賴于其他項目鲫趁;類似的,任務可以形成一個依賴關系圖來確保他們的執(zhí)行順序利虫。
腳步基本組成部分
項目(project)
??一個項目代表一個正在構建的組件(比如一個jar文件)挨厚,當構建啟動后,Gradle會基于build.gradle實例化一個org.gradle.api.Project類糠惫,并且能夠通過project變量使其隱式可用疫剃。Project類的主要屬性和方法
??屬性group、name硼讽、version
??方法有apply巢价、dependencies、repositories固阁、task
??屬性的其他配置方式:ext壤躲、gradle.properties
??任務(task)
??任務對應org.gradle.api.Task。主要包括任務動作和任務依賴备燃。任務動作定義了一個最小的工作單元碉克。可用定義依賴于其他任務并齐、動作序列和執(zhí)行條件漏麦。
??**Task重要的方法
????dependsOn
????doFirst,doLast << **
?? task是個動作列,doFirst就是在動作列表最前面添加一個動作况褪,doLast就是在動作列表的最后面添加一個動作
?? 自定義任務(Task)撕贞、Task的生命周期
?? 自定義創(chuàng)建文件夾任務
??構建生命周期
??1、 初始化階段
??項目構建開始的時候测垛,會根據(jù)build.gradle構建一個項目即project并且在這個腳本中隱式可用捏膨。在多項目構建中這個階段也是很重要的,它會初始化所有需要參與到構建中的項目食侮。
??2号涯、配置階段
??這個階段就是遍歷項目中所有task,生成task依賴順序以及執(zhí)行順序熬北,根據(jù)配置代碼來生成的。配置代碼就是除了動作代碼外都是配置代碼诚隙,可以簡單的這么理解讶隐。 這個階段相當于初始化任務Task階段
??配置代碼如:
?? 3、執(zhí)行階段
??主要執(zhí)行動作代碼久又,執(zhí)行完后即一個構建就完成了巫延。
??動作代碼如:
??Gradle的工作流程其實蠻簡單,用一個圖15來表達:
??圖15告訴我們地消,Gradle工作包含三個階段:
??首先是初始化階段炉峰。對我們前面的multi-project build而言,就是執(zhí)行settings.gradle
??Initiliazation phase的下一個階段是Configration階段脉执。
??Configration階段的目標是解析每個project中的build.gradle疼阔。比如multi-project build例子中,解析每個子目錄中的build.gradle半夷。在這兩個階段之間婆廊,我們可以加一些定制化的Hook。這當然是通過API來添加的巫橄。Configuration階段完了后淘邻,整個build的project以及內部的Task關系就確定了。恩湘换?前面說過宾舅,一個Project包含很多Task,每個Task之間有依賴關系彩倚。Configuration會建立一個有向圖來描述Task之間的依賴關系筹我。所以,我們可以添加一個HOOK帆离,即當Task關系圖建立好后蔬蕊,執(zhí)行一些操作。
??最后一個階段就是執(zhí)行任務了盯质。當然袁串,任務執(zhí)行完后概而,我們還可以加Hook呼巷。
我在:
- settings.gradle加了一個輸出。
- 在posdevice的build.gradle加了圖15中的beforeProject函數(shù)赎瑰。
- 在CPosSystemSdk加了taskGraph whenReady函數(shù)和buidFinished函數(shù)王悍。
好了,Hook的代碼怎么寫餐曼,估計你很好奇压储,而且肯定會埋汰鲜漩,怎么就還沒告訴我怎么寫Gradle。馬上了集惋!
最后孕似,關于Gradle的工作流程,你只要記坠涡獭:
- Gradle有一個初始化流程喉祭,這個時候settings.gradle會執(zhí)行。
- 在配置階段雷绢,每個Project都會被解析泛烙,其內部的任務也會被添加到一個有向圖里,用于解決執(zhí)行過程中的依賴關系翘紊。
- 然后才是執(zhí)行階段蔽氨。你在gradle xxx中指定什么任務,gradle就會將這個xxx任務鏈上的所有任務全部按依賴順序執(zhí)行一遍帆疟!
??下面的這個鏈接對于學習Gradle很重要鹉究,
??https://docs.gradle.org/current/dsl/
依賴管理
幾乎所有的基于JVM軟件項目都需要依賴外部類庫來重用現(xiàn)有的功能。自動化依賴管理可以明確依賴的版本踪宠,可以解決因傳遞性依賴帶來的版本沖突坊饶。
工件坐標
group、name殴蓬、version
** 倉庫**
mavenLocal/mavenCentral/jcenter匿级,第一個本地倉庫,后面兩個是公共倉庫
自定義maven倉庫染厅,就是maven私服倉庫痘绎,公司內部為了代碼的安全肯定不會放到公共倉庫里面去,我們需要搭建一個內部倉庫肖粮,管理自己的jar包孤页。這個是實際中經常用的。
文件倉庫涩馆,所謂的文件倉庫行施,就是本地機器上的文件路徑也可以作為倉庫,這個非常不建議大家使用魂那,因為我們使用構建工具就是為了讓構建一致性蛾号,就是到處構建,結果應該是一樣的涯雅。如果跟具體的機器有關的話鲜结,就違反了我們使用構建工具的初衷。
依賴的傳遞性
B依賴A,如果C依賴B精刷,那么C依賴A
正是因為有這種依賴的傳遞性拗胜,造成版本的沖突
??依賴階段配置
- compile、runtime
- testCompile怒允、testRuntime
依賴階段關系
??編譯期依賴的埂软,運行期必然依賴,運行期依賴的纫事,編譯期未必依賴仰美;源碼編譯依賴的,測試編譯必然依賴儿礼,測試編譯依賴的咖杂,源碼編譯期未必依賴;測試編譯依賴的蚊夫,測試運行期必然依賴诉字。
??mavenCentral公共倉庫網址http://search.maven.org/
??** 版本沖突解決方法**
?? 版本沖突實際列子
?? 解決方法步驟
?? 1、查看依賴報告
?? 2知纷、排除傳遞性依賴
?? 3壤圃、強制制定一個版本
?? 基本不需要我們自己解決版本沖突,gradle會自動幫我們強制依賴最高版本的jar包
?? 修改默認解決策略方法琅轧,不然很難發(fā)現(xiàn)版本沖突
configurations.all{
resolutionsStrategy{
failOnVersionConflict()
}
}
排除傳遞性依賴的方法如下
compile('org.hibernate:hibernate-core:3.6.3.Final'){
exclude group:"org.slf4j",module:"slf4j-api"
//transitive=false
}
強制制定一個版本
configurations.all{
resolutionsStrategy{
force 'org.slf4j:slf4j-api:1.7.24'
}
}