本身動手應用Groovy實現Gradle的DSL(一) Task定義

轉載請註明出處: http://my.oschina.net/u/874727/blog/741610java

若有問題能夠加Q: 1025250620
 緩存

Groovy是基於JVM的敏捷開發語言之一。因爲它的語法特性,經常用來構建領域性的DSL。咱們今天的另一個主角就是經過Groovy所構建出來的一門DSL語言Gradle。若是是用過Apache的ant還有Maven等項目自動化構建工具,相信你聽過甚至已經逐漸往Gradle方向轉換。因爲Gradle使用了Groovy的基本語法,使得結構更加清晰,緊湊,更加方便理解。本篇文章會涉及到一些Groovy的語法(做者假定各位都是瞭解Groovy語法和使用過Gradle的開發者)。數據結構

咱們先來看一下簡單的Gradle腳本。咱們知道按照Gradle的既有約定,Gradle的腳本通常寫在build.gradle的配置文件中,其中以task爲基本單位。閉包

task helloworld {  
        println "helloworld!!"  
}//結構1  
  
task sayHello<< {  
        println "sayHello!!"  
}//結構2  

這裏,若是咱們把task理解爲方法的話,咱們能夠假設task方法所傳入的參數就是一個Task對象。咱們先抽象出來一個簡單的Task對象(POGO對象)。經過第一個結構:工具

task helloworld {  
        println "helloworld!!"  
}  

咱們看出Task有一個執行閉包,還有一個基本屬性name。而這種結構中「helloworld」能夠理解爲一個Groovy方法名,而閉包做爲這個方法的參數。但因爲"helloworld"這個名字是由用戶本身定義的,所以須要用到Groovy的MOP知識,調用動態方法。這裏咱們用invokeMethod方法來實現。綜上咱們所定義的內容咱們先實驗一個簡單的代碼:gradle

def task(Task t) {  
  
}  
  
class Task {  
        def name  
        def closure  
}  
  
def invokeMethod(String name,args) {  
  
        println "call method $name()"  
}  
  
task helloworld {  
        println "helloworld!!"  
}  

咱們經過命令行看到輸出:"call method helloworld()"。接下去咱們要爲結構1細化Task的構建流程,也就是細化你的invoke方法。咱們使用invokeMethod來動態調用方法的缺點在於方法並非真正的動態注入(若是須要真正的調用可使用metaClass機制),而是經過AOP攔截的方式來攔截調用。當你定義了一個「helloworld」的Task了之後,後面可能還須要用到。那麼那個時候你的程序仍是會調用invokeMethod方法來構造。爲了不重複構造或者查詢異常,咱們須要有一個機制來緩存咱們已經構造好的Task。這裏,我定義一個TaskPool數據結構來享元咱們的task。ui

class TaskPool {  
        def map = [:]  
        def push(String name,Task t) {  
                map.put(name,t)  
        }  
  
        def get(String name) {  
                map.get(name)  
        }  
}  
  
taskPool = new TaskPool()  


invokeMethod方法能夠改成:this

def invokeMethod(String name,args) {  
        println "call method $name()"  
        t = taskPool.get(name)  
        if (t == null) {  
                if (args[0] instanceof Closure) {  
                        t = new Task(name:name,closure:args[0])  
                }  
        }  
        t  
}  


能夠看到,這裏實際上我給了一個類型爲Task的返回值t。給出這個類型的目的是爲了提供給task方法使用。而直到這裏,咱們能夠把「Task push Pool」這個操做放在Task create後的任意位置,可是我把「Task push Pool」的操做放到task方法中,由於咱們在使用結構2"<<"的時候須要經過另一個動態調用入口。咱們還須要給Task類增長一個execute方法來運行咱們task的閉包參數:.net

class Task {  
        def name  
        def closure  
        def execute() {  
                println "task $name start >>"  
                closure()  
                println "task $name end <<"  
        }  
}  

 

def task(Task t) {  
        taskPool.push(t.name,t)  
        t.execute()  
}  

好了,咱們趕忙敲一下groovy命令來實驗一下咱們的結構1吧:命令行

call method helloworld()  
task helloworld start >>  
helloworld!!  
task helloworld end <<  


你是否獲得了跟我同樣的結果呢?這樣咱們經過groovy來實現告終構1樣式的task。那麼咱們該如何實現結構2範式呢?

 

咱們來回顧一下結構2範式:

task sayHello<< {  
        println "hello!! david!"  
}  
  1.  

task以後咱們仍是要傳入一個Task的變量,而"<<"是對象內的操做,這樣咱們就不能再把sayHello做爲方法,而應該做爲一個參數變量來看,而這個參數變量的類型很顯然是Task類型。所以咱們先在咱們的Task類中重載咱們的"<<"符號:

class Task {  
        def name  
        def closure  
        def leftShift(c) {  
                this.closure = c  
                this  
        }
        def execute() {  
                println "task $name start >>"  
                closure()  
                println "task $name end <<"  
        }  
}  

而做爲參數變量的sayHello咱們須要調用Groovy的另一個動態接口getProperty(String name)。類中全部獲取屬性的調用都會被這個方法所攔截,咱們按照與invokeMethod相同的模式來實現它:

 

def getProperty(String name) {  
  
        def t = taskPool.get(name)  
        if (t == null) {  
                t = new Task(name:name)  
        }  
        t  
}  

但這樣是有問題的,爲何呢?由於你的taskPool也是做爲你的屬性,這種狀況下你頗有可能形成遞歸堆棧溢出,那麼該怎麼解決呢?

 

因爲你的taskPool變量屬於你原有屬性,所以須要調用你原有的調用過程。因此須要將代碼改成:

def getProperty(String name) {  
        def t  
        try {  
                t  = super.getProperty(name)  
        } catch(e) {  
  
                t = taskPool.get(name)  
                if (t == null) {  
                        t = new Task(name:name)  
                }     
        }     
        t     
}  

下面咱們用咱們的兩種結構來實驗一下咱們定義的DSL吧:

task helloworld {  
        println "helloworld!!"  
}  
task sayHello<< {  
        println "hello!! david!"  
}  


最後輸出的結果爲:

task helloworld start >>  
helloworld!!  
task helloworld end <<  
  
task sayHello start >>  
hello!! david!  
task sayHello end << 


轉載請註明出處: http://my.oschina.net/u/874727/blog/741610

若有問題能夠加Q: 1025250620

相關文章
相關標籤/搜索