使用ExpandoMetaClass和Category可以實現元編程。ExpandoMetaClass可以動態添加和修改類的構造器,方法和屬性。Category只能動態添加和修改類的方法。
當使用EMC動態添加或修改方法時,閉包Closure作爲方法體被傳入,它的delegate指向調用此方法的實例。
具體使用可參考:http://www.groovyq.net/node/75
1、爲類添加方法(爲String類添加String times(int count)方法)
String.metaClass.times = {int count ->
String str = ""
for (int i = 0; i < count; i++) {
str += delegate
}
return str
}
assert "hellohellohello", "hello".times(3)
2、爲類添加重載方法(爲String類添加String times()方法)
2、爲類添加重載方法(爲String類添加String times()方法)
String.metaClass.times << {->
return delegate
}
3、爲類修改方法(將String類的toUpperCase改成toLowerCase)
4、反射拿到方法的元型
5、爲單個實例添加或修改方法
6、一次添加多個方法
3、爲類修改方法(將String類的toUpperCase改成toLowerCase)
String.metaClass.toUpperCase = {->
return delegate.toLowerCase()
}
assert "hello", "heLLO".toUpperCase()
4、反射拿到方法的元型
String.metaClass.invokeMethod = {String mName, mArgs ->
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 ->
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))
9、爲類添加屬性
7、添加或修改靜態方法(爲String類添加static String twice(String val))
String.metaClass.static.twice = {String val ->
return val + val
}
assert "hellohello", String.twice("hello")
8、添加或修改構造器
8、添加或修改構造器
class MOP {
String val
def print() {
println val
}
}
MOP.metaClass.constructor << {String val ->
return new MOP(val: val)
}
new MOP("hello").print()
9、爲類添加屬性
MOP.metaClass.title = "title"
10、爲類添加只讀屬性(即只添加get方法)
10、爲類添加只讀屬性(即只添加get方法)
MOP.metaClass.getTitle = {-> "title"}
11、去掉元編程,只需將metaClass置爲null即可String.metaClass = null
12、可以在使用static方法時,用use塊簡化代碼
既然EMC提供了運行期動態添加修改方法和屬性的能力,我們可以用在什麼地方呢?
1、有時候爲了調試經常會調用System.out.println(XXXX),每次寫這麼長太煩了,如果變成XXXX.out()是不是就方便多了。
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 = {->
System.out.println(delegate)
}
"hello".out()
2、有時候爲了從url獲取數據(http GET方法),要是使用HttpClient得寫一堆代碼,如果變成調用"$url".httpGet()能直接獲取響應報文該多爽啊。其實只需要給String添加一個httpGet方法即可:String.metaClass.httpGet = {->
3、同樣在實現執行系統命令的時候也可以這麼做。類似這種需求是無窮無盡的,都可以用MOP簡單實現。3.times { println "hello!" }list << "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類配置。這樣就可以用配置方式實現功能開關了。
2、有時候爲了從url獲取數據(http GET方法),要是使用HttpClient得寫一堆代碼,如果變成調用"$url".httpGet()能直接獲取響應報文該多爽啊。其實只需要給String添加一個httpGet方法即可:String.metaClass.httpGet = {->
//用HttpClient實現}
3、同樣在實現執行系統命令的時候也可以這麼做。類似這種需求是無窮無盡的,都可以用MOP簡單實現。3.times { println "hello!" }list << "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(略)
還有更多的應用場景,希望大家一塊討論。