轉載請註明出處: 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!" }
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