Gradle是一種聲明式的構建工具。在執行時,Gradle並不會一開始便順序執行build.gradle文件中的內容,而是分爲兩個階段,第一個階段是配置階段,而後纔是實際的執行階段。在配置階段,Gradle將讀取全部build.gradle文件的全部內容來配置Project和Task等,好比設置Project和Task的Property,處理Task之間的依賴關係等。html
雖然不少時候咱們只須要照着網上的例子寫本身的DSL語句就好了,可是此時咱們所知道的也就只有這麼多了。若是咱們可以瞭解Gradle DSL的內部工做機制,那麼咱們即可以達到觸類旁通的效果。在前面的文章中咱們講到,Gradle的DSL只是Groovy語言的內部DSL,也即必須遵循Groovy的語法規則。如今,讓咱們先來看看如下很是簡單的Task:閉包
task showDescription1 << { description = 'this is task showDescription' println description } task showDescription2 << { println description } showDescription2.description = 'this is task showDescription' task showDescription3 << { println description } showDescription3 { description = 'this is task showDescription' }
以上3個Task完成的功能均相同,即先設置Task的description屬性,在將其輸出到命令行。可是,他們對description的設置方式是不一樣的。對於showDescription1,咱們在定義一個Task的同時便設置description;對於showDescription2,其自己即是Project的一個Property;而對於showDescription3,咱們是在一個和它同名的方法中設置description。app
事實上,對於每個Task,Gradle都會在Project中建立一個同名的Property,因此咱們能夠將該Task看成Property來訪問,showDescription2即是這種狀況。另外,Gradle還會建立一個同名的方法,該方法接受一個閉包,咱們可使用該方法來配置Task,showDescription3即是這種狀況。工具
要讀懂Gradle,咱們首先須要瞭解Groovy語言中的兩個概念,一個Groovy中的Bean概念,一個是Groovy閉包的delegate機制。gradle
Groovy中的Bean和Java中的Bean有一個很大的不一樣,即Groovy爲每個字段都會自動生成getter和setter,而且咱們能夠經過像訪問字段自己同樣調用getter和setter,好比:ui
class GroovyBeanExample { private String name } def bean = new GroovyBeanExample() bean.name = 'this is name' println bean.name
咱們看到,GroovyBeanExample只定義了一個私有的name屬性,並無getter和setter。可是在使用時,咱們能夠直接對name進行訪問,不管時讀仍是寫。事實上,咱們並非在直接訪問name屬性,當咱們執行"bean.name = 'this is name'"時,咱們實際調用的是"bean.setName('this is name')",而在調用"println bean.name"時,咱們實際調用的是"println bean.getName()"。這裏的緣由在於,Groovy動態地爲name建立了getter和setter,採用像直接訪問的方式的目的是爲了增長代碼的可讀性,使它更加天然,而在內部,Groovy依然是在調用setter和getter方法。這樣,咱們即可以理解上面對showDescription2的description設置原理。this
另外,Gradle大量地使用了Groovy閉包的delegate機制。簡單來講,delegate機制可使咱們將一個閉包中的執行代碼的做用對象設置成任意其餘對象。好比:spa
class Child { private String name } class Parent { Child child = new Child(); void configChild(Closure c) { c.delegate = child c.setResolveStrategy Closure.DELEGATE_FIRST c() } } def parent = new Parent() parent.configChild { name = "child name" } println parent.child.name
在上面的例子中,當咱們調用configChild()方法時,咱們並無指出name屬性是屬於Child的,可是它的確是在設置Child的name屬性。事實上光從該方法的調用中,咱們根本不知道name是屬於哪一個對象的,你可能會認爲它是屬於Parent的。真實狀況是,在默認狀況下,name的確被認爲是屬於Parent的,可是咱們在configChild()方法的定義中作了手腳,使其再也不訪問Parent中的name(Parent也沒有name屬性),而是Child的name。在configChild()方法中,咱們將該方法接受的閉包的delegate設置成了child,而後將該閉包的ResolveStrategy設置成了DELEGATE_FIRST。這樣,在調用configChild()時,所跟閉包中代碼被代理到了child上,即這些代碼其實是在child上執行的。此外,閉包的ResolveStrategy在默認狀況下是OWNER_FIRST,即它會先查找閉包的owner(這裏即parent),若是owner存在,則在owner上執行閉包中的代碼。這裏咱們將其設置成了DELEGATE_FIRST,即該閉包會首先查找delegate(本例中即child),若是找到,該閉包便會在delegate上執行。對於上面的showDescription3,即是這種狀況。固然,實際狀況會稍微複雜一點,好比showDescription3()方法會在內部調用showDescription3的configure()方法,再在configure()方法中執行閉包中的代碼。命令行
你可能會發現,在使用Gradle時,咱們並無像上面的parent.configChild()同樣指明方法調用的對象,而是在build.gradle文件中直接調用task(),apply()和configuration()方法等,這是由於在沒有說明調用對象的狀況下,Gradle會自動將調用對象設置成當前Project。好比調用apply()方法和調用project.apply()方法的效果是同樣的。查查Gradle的Project文檔,你會發現這些方法都是Project類的方法。代理
另外舉個例子,對於configurations()方法(它的做用咱們將在後面的文章中講到),該方法實際上會將所跟閉包的delegate設置成ConfigurationContainer,而後在該ConfigurationContainer上執行閉包中的代碼。再好比,dependencies()方法,該方法會將所跟閉包的delegate設置成DependencyHandler。
還有,Project還定義了configure(Object object,Closure configureClosure)方法,該方法是專門用來配置對象的(好比Task),它會將configureClosure的delegate設置成object,以後configureClosure中的執行代碼實際上是在object上執行的。和Groovy Bean同樣,delegate機制的一個好處是能夠增長所建立DSL的可讀性。