MOP 總結
之前介紹了基于 MOP 技術:
接下來對 MOP 的這三種技術的使用做一個匯總
在運行時創建類。
經過之前的講解,我們可以發現,正是 MOP 的出現,使得 Groovy 變得非常靈活,可以改變已有類的結構,攔截或添加方法等,這為我們的開發工作提供了極大的便利性。既然可以擴展已有的類,那么我們也可以借助 MOP 來動態的創建一個類。這里需要借助 MOP 為我們提供的而一個類——Expando 類。
man = new Expando()
man.name = 'zx'
man.age = 18
man.work = { "working" }
println man
println man.work()
Expando 像是一個可以容納任何屬性的類,我們可以為其添加任意的屬性,任意的方法(以閉包的形式),其實無論是屬性,還是閉包,都被保存在 Expand 中的 Map 中,key 對應屬性\閉包名,value 對應屬性值,或者閉包體
關于攔截,注入,合成
經過之前三篇的學習,我們了解到,無論是攔截,注入,還是合成。每種技術都有若干實現方式。但是都不可避免的用到 MetaClass 這個類。攔截時,向其中注入了 invokeMethod 方法;合成時,向其中注入了 methodMissing 方法。注入的都是閉包,但是調用時如果找不到對應的方法,那么就會從閉包中找對應的名字,同時閉包的參數要和對應的方法相同。
- 對于攔截,如果我們能修改類,就實現 GroovyInterceptable 接口,并實現 invokeMethod 方法;否則就在 ClassName.metaClass 上注入 invokeMethod
- 對于合成,如果我們能修改類,就在類中重寫 methodMissing 方法;否則就在 ClassName.metaClass 上注入 methodMissing
- 對于注入,如果注入的方法在之前的類中已經實現過了,同時又在 ClassName.metaClass 上進行了注入,那么 metaClass 上的方法會優先執行。
相信到這里在看這幅圖應該更容易理解:
一個綜合應用
之前將的 MOP 的三大技術是分開來講的,現在把這三個技術融合起來,實現這樣一個場景,對于一個類,我們調用其方法,如果存在,就執行其本身的方法,如果不存在就進行方法合成。
class Man {
def work(){
"I can work"
}
}
Man.metaClass.invokeMethod = {
String name,args ->
def method = Man.metaClass.getMetaClass(name,args)
if(method){
method.invoke(delegate,args)
}else{
Man.metaClass.invokeMissingMethod(delegate,name,args)
//或者執行下面的方法,metaClass 中如果沒有 invokeMethod 方法,那么會調用 metaClass 中的 methodMissing 方法,但是不如上面直接調用來的直觀
//Man.metaClass.invokeMethod(delegate,name,args)
}
}
Man.metaClass.methodMissing = {
String name,args ->
def skills = ["run","sing","dance","talk"];
if (skills.contains(name)){
def impl = {
Object[] vars ->
"I can " + name
}
Man.metaClass."$name" = impl
impl(args)
}else{
throw new MissingMethodException(name,Man.class,args)
}
在這個例子中,首先使用攔截技術,將所有的方法調用攔截住(MetaClass 注入 invokeMethod 方法),然后分析本類是否可以執行該方法,如果可以路由給原本的方法,如果不可以那么執行合成(MetaClass 注入 methodMissing 方法)。
這里注意到一個細節:我們在方法合成之后,先將合成的方法注入到 MetaClass 中,這樣在第二次調用的時候,就不會在此合成了,而是直接調用,大大提高的執行效率