Gradle是基於Groovy的DSL基礎上的構建工具,Gradle中的閉包,其原型上實際上即Groovy中閉包。而在表現形式上,其實,Gradle更多的是以約定
和基於約定基礎上的配置
去展示。但本質上,大多數配置
,實際上都對應着閉包以及閉包的具體使用。java
例如,實際Android項目中,咱們常常看到相似以下的所謂配置項:web
allprojects {
repositories {
mavenLocal()
maven {
url 'http://maven.aliyun.com/nexus/content/groups/public/' } google() jcenter() } configurations.all { resolutionStrategy.cacheChangingModulesFor 1, 'seconds' } } task clean(type: Delete) { delete rootProject.buildDir delete "${rootProject.rootDir}/jenkinsOut" } 複製代碼
當咱們在allprojects
上按住command鍵時,發現有以下圖所示的提示。bash
出現的提示指的是此配置項所對應的Gradle中原型,能夠點擊直接進入對應的Gradle API。閉包
/**
* <p>Configures this project and each of its sub-projects.</p>
*
* <p>This method executes the given closure against this project and its sub-projects. The target {@link Project}
* is passed to the closure as the closure's delegate.</p> * * @param configureClosure The closure to execute. */ void allprojects(Closure configureClosure); 複製代碼
咱們發現,咱們經常使用的allprojects
配置,實際上真正對應着的,是一個void allprojects(Closure configureClosure)
Java方法,而其後{}
中的配置,實際上總體是一個Closure
類型的參數,在方法說明中,指出這個方法是爲當前項目及其子項目執行給定的閉包,目標@Project做爲閉包的委託傳遞給閉包
。app
因而,到底什麼是閉包,閉包具體的運做機制是怎麼樣的,有必要實際窺探一番。maven
點擊Gradle API中的Closure,能夠進入對應的Closure類型聲明,實際上對應的是Groovy jar包中的class文件聲明。函數
package groovy.lang;
import ...
public abstract class Closure<V> extends GroovyObjectSupport implements Cloneable, Runnable, GroovyCallable<V>, Serializable {
複製代碼
Closure
,翻譯過來是閉包
,在JS等語言中也存在閉包的概念,可是,不一樣語言中,對於閉包的具體描述或實際的應用,不一樣語言,可能還有所不一樣。工具
先了解一下Groovy
中閉包
的描述:post
閉包,是一個代碼塊,或能夠理解成一個匿名函數,在外部方法調用時,能夠將其做爲方法的實參傳遞給方法的形參,並在方法內部回調此匿名函數,且回調此匿名函數時能夠傳遞實參給到匿名函數的內部去接收,並執行此匿名函數。
同時,此代碼塊或匿名函數也能夠賦值給一個變量,使其具備自執行的能力,且最後一行的執行語句做爲匿名函數的返回。
複製代碼
看着好像不太容易理解,能夠具體看幾個實際例子。ui
// 1,定義一個閉包,賦值給一個變量,並進行顯示的自我調用。
def t = {
println "Hello Closure" } // 此處也能夠寫成t.call() t() // 運行後,輸出結果爲: Hello Closure 複製代碼
其中,以變量的方式調用閉包t()
與t.call()
是等價的。
// 2,定義一個閉包,賦值給一個變量,並進行顯示的自我調用,並檢測其返回值
def t = {
println "Hello Closure" "ttt" } println "closure return: " + t.call() // 運行後,輸出結果爲: Hello Closure closure return: ttt 複製代碼
// 3,定義一個閉包,賦值給一個變量,並進行顯示的自我調用,調用時向閉包傳遞實參
def t = {
println "Hello Closure, the param value is: " + it } t("mm") // 運行後,輸出結果爲: Hello Closure, the param value is: mm 複製代碼
調用閉包時,若是向閉包傳遞實參,閉包內部若是沒有聲明形參接收,默認是以it的變量的一個形參去接收實參。
所以,例3其實是等價於:
def t = {
it ->
println "Hello Closure, the param value is: " + it } t("mm") 複製代碼
// 4,若是閉包中顯示的聲明瞭形參,則以顯示的聲明的形參去接收實參
def t = {
x, y ->
println "Hello Closure, the param value is: " + x + ", " + y } t("mm", "nn") // 運行後,輸出結果爲: Hello Closure, the param value is: mm, nn 複製代碼
以上,都是將閉包賦值給一個變量後,進行的閉包的調用行爲。
同時,咱們也能夠將閉包做爲一個方法實參,在方法調用時傳遞給方法形參,而後方法內部造成對此閉包的回調。
// 5,將閉包做爲一個方法實參,在方法調用時傳遞給方法形參,而後方法內部造成對此閉包的回調
class Person {
String getName(Closure closure) {
closure("cc", "dd") } } def t = { x, y -> println "Hello Closure, the param value is: " + x + ", " + y } new Person().getName(t) // 運行後,輸出結果爲: Hello Closure, the param value is: cc, dd 複製代碼
例5中的閉包若是沒有事先賦值給變量t
,而也能夠直接使用,效果等價於:
class Person {
String getName(Closure closure) {
closure("cc", "dd") } } new Person().getName({ x, y -> println "Hello Closure, the param value is: " + x + ", " + y }) 複製代碼
閉包做爲方法中的最後一個參數,能夠從()
中拿出來,則等價於:
new Person().getName(){ x, y -> println "Hello Closure, the param value is: " + x + ", " + y } 複製代碼
同時,方法後的()
能夠去掉,則等價於:
new Person().getName {
x, y ->
println "Hello Closure, the param value is: " + x + ", " + y } 複製代碼
若是外部調用閉包的方法傳遞實參時,沒有傳遞實參或只傳遞了一個參數(若是沒有傳遞實參,則it爲null),則進一步演化成:
class Person {
String getName(Closure closure) {
closure("cc") } } new Person().getName { println "Hello Closure, the param value is: " + it } 複製代碼
這也就是咱們在Gradle中常常見到的閉包形式,即表面上只有{}
的配置形式。
將閉包理解成一個特殊的匿名函數,不管是經過變量的自調用,仍是做爲方法實參的傳遞後,在方法內部被回調,閉包的最後一行執行被當作匿名函數的總體返回,均可以很好的得以理解。同時,也能容易的理解閉包能夠嵌套使用等(即當作匿名函數的嵌套)。
如:以Gradle中可能常常見到的each
寫法爲例:
dirs.each { dir ->
java.srcDir("src/$dir/java") res.srcDir("src/$dir/res") } 複製代碼
實際上內部對應的執行過程爲:
public static <T> List<T> each(List<T> self, @ClosureParams(FirstGenericType.class) Closure closure) {
return (List)each((Iterable)self, closure); } public static <T> Iterable<T> each(Iterable<T> self, @ClosureParams(FirstGenericType.class) Closure closure) { each(self.iterator(), closure); return self; } public static <T> Iterator<T> each(Iterator<T> self, @ClosureParams(FirstGenericType.class) Closure closure) { while(self.hasNext()) { Object arg = self.next(); closure.call(arg); } return self; } 複製代碼
顯然,內部最終經過closure.call(arg)
回調了閉包自身,並向閉包傳遞了實參。
閉包在Gradle
中的配置中,被大量使用。理解Gradle
中的閉包,對一些特殊的寫法,如Gradle構建生命週期中的閉包回調中的實參使用等,能夠有很好的運用。
如常見的在Gradle
構建的配置階段
中的afterEvaluate
hook
中,能夠設置相關task
的依賴關係等。此時,it
接收的是回傳進來的當前project
實參。
afterEvaluate {
...
Task assembleJenkinsTask = rootProject.tasks.getByName('assembleJenkins') Task unitTestTask = it.tasks.findByName('testDebugUnitTest') if (unitTestTask != null) { assembleJenkinsTask.dependsOn unitTestTask } ... } 複製代碼
在必定意義上,Groovy
中閉包的概念,以及其實際的用法上,實質上根Java 8
中的lambda
表達式具備很相近的含義。