Groovy元編程MOP(ExpandoMetaClass:EMC)的應用場景

使用ExpandoMetaClass和Category可以實現元編程。ExpandoMetaClass可以動態添加和修改類的構造器,方法和屬性。Category只能動態添加和修改類的方法。

當使用EMC動態添加或修改方法時,閉包Closure作爲方法體被傳入,它的delegate指向調用此方法的實例。

具體使用可參考:http://www.groovyq.net/node/75

 

1、爲類添加方法(爲String類添加String times(int count)方法)

String.metaClass.times = {int count -&gt; String str = "" for (int i = 0; i < count; i++) { str += delegate } return str } assert "hellohellohello", "hello".times(3)
2、爲類添加重載方法(爲String類添加String times()方法)
String.metaClass.times &lt;&lt; {-> return delegate }
3、爲類修改方法(將String類的toUpperCase改成toLowerCase)
String.metaClass.toUpperCase = {-&gt; return delegate.toLowerCase() } assert "hello", "heLLO".toUpperCase()

4、反射拿到方法的元型
String.metaClass.invokeMethod = {String mName, mArgs -&gt; def m = String.metaClass.getMetaMethod(mName, mArgs) if (mName != "concat" && mName != "toUpperCase") return m.invoke(delegate, mArgs) long s = System.currentTimeMillis(); def result = m.invoke(delegate, mArgs) long e = System.currentTimeMillis(); long duration = e - s; println("MOP耗費時間:" + duration); return result; }

5、爲單個實例添加或修改方法
String instance = "hi" instance.metaClass.times = {int count -&gt; String str = "" for (int i = 0; i < count; i++) { str += delegate } return str } println instance.times(3) println "he".times(3) // MissingMethodException

6、一次添加多個方法
String.metaClass { times {int count -> String str = "" for (int i = 0; i < count; i++) { str += delegate } return str } toUpperCase {-> return delegate.toLowerCase() } }
7、添加或修改靜態方法(爲String類添加static String twice(String val))
String.metaClass.static.twice = {String val -&gt; return val + val } assert "hellohello", String.twice("hello")

8、添加或修改構造器
class MOP { String val def print() { println val } } MOP.metaClass.constructor <&lt; {String val -> return new MOP(val: val) } new MOP("hello").print()
 

9、爲類添加屬性
MOP.metaClass.title = "title"
10、爲類添加只讀屬性(即只添加get方法)
MOP.metaClass.getTitle = {-&gt; "title"}
11、去掉元編程,只需將metaClass置爲null即可String.metaClass = null
12、可以在使用static方法時,用use塊簡化代碼
use(org.apache.commons.lang.time.DateUtils) { new Date().addDays(1) //這裏實際調用的是static Date addDays(Date date,int amount)方法 }也可以一次使用多個類的static方法
use(org.apache.commons.lang.time.DateUtils,org.apache.commons.lang.StringUtils) { new Date().addDays(1) "stringUtils".capitalise() }

既然EMC提供了運行期動態添加修改方法和屬性的能力,我們可以用在什麼地方呢?
1、有時候爲了調試經常會調用System.out.println(XXXX),每次寫這麼長太煩了,如果變成XXXX.out()是不是就方便多了。
只需要爲String、Integer等類添加一個out方法即可實現:
String.metaClass.out = {-&gt; System.out.println(delegate) } "hello".out()
2、有時候爲了從url獲取數據(http GET方法),要是使用HttpClient得寫一堆代碼,如果變成調用"$url".httpGet()能直接獲取響應報文該多爽啊。其實只需要給String添加一個httpGet方法即可:String.metaClass.httpGet = {-&gt;
//用HttpClient實現
}
3、同樣在實現執行系統命令的時候也可以這麼做。類似這種需求是無窮無盡的,都可以用MOP簡單實現。3.times { println "hello!" }list &lt;&lt; "a string"def range = 1..9。。。。。。Groovy裏面這些看似很神奇的語法其實都是通過MOP實現的。
4、用MOP實現AOP用MOP可以實現方法攔截,對源代碼絲毫沒有侵入性。比如想知道String的toUpperCase方法執行效率就可以用MOP。也可以捕獲異常輸出到日誌中,而不影響源代碼的執行。但是需要注意,如果應用層是用反射調用執行的,MOP就攔截不到了。
5、配置開關比如系統要構建xml,調試時爲了xml語意清晰可以在生成xml時加上comment備註,但是系統上線真正運營時這些comment就沒什麼用了,這時可以用MOP關掉commment。具體代碼自己實現。可以將這個功能寫成一個函數,系統啓動是調一下這個函數就關閉comment了。函數調用可以用Spring類配置。這樣就可以用配置方式實現功能開關了。

 

6、DSL(略)

 

還有更多的應用場景,希望大家一塊討論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章