我們很早就會使用Groovy語言的hook,即"invokeMethod"方法和其他的幾個方法。我們會在一個類中實現"invokeMethod"方法,用來分派所有的或部分的在運行期內調用的該類實例的方法。這些我們在《Groovy探索之MOP 一 invokeMethod和methodMissing方法》已經詳細的談到過。
現在,我們已經深入的接觸到了Groovy語言的MetaClass,更是也到處使用到了ExpandoMetaClass。我們都已經知道,ExpandoMetaClass的靈活性,不是hook能夠比擬的。我們就想,既然使用ExpandoMetaClass能夠在運行期內給一個類增加方法,那麼,我們是否也可以在運行期內增加hook方法呢?
我們可以回答,是。還是先來看一個簡單的例子吧!
我們還是有這麼一個簡單的類:
class Foo
{
def foo()
{
'foo'
}
}
我們現在嘗試著在運行期內增加"invokeMethod"方法:
Foo.metaClass.invokeMethod = {
String name,args1 ->
'test'
}
接著,我們來測試這個運行期內增加的"invokeMethod"方法:
def foo = new Foo()
println foo.foo()
println foo.test()
運行結果為:
test
test
可以看出,我們在運行期內增加的"invokeMethod"方法攔截了Foo類的實例的所有的方法。
當然,如果我們想把"invokeMethod"方法攔截的方法中,已經存在於Foo類的方法,如"foo",交給Foo類本身的方法來處理。那麼,我們就可以如下實現"invokeMethod"方法:
Foo.metaClass.invokeMethod = {
String name,args1 ->
def m = Foo.metaClass.getMetaMethod(name,args1)
def result
if(m)
result = m.invoke(delegate,args1)
else
result = 'test'
result
}
我們再運行上面的測試代碼,就會得到如下的結果:
foo
test
現在,我們來定義幾個名詞,我們把類本身實現的"invokeMethod"方法稱為對象的"invokeMethod"方法,而把在運行期內通過ExpandoMetaClass增加的"invokeMethod"方法稱為metaClass的"invokeMethod"方法。
現在的問題是,如果一個對象,同時實行了對象的"invokeMethod"方法和metaClass的"invokeMethod"方法,那麼該調用誰?
你肯定會說,這個問題很簡單啊,我們來做一個簡單的測試不就知道了。好,現在,我們先來看看這個簡單的測試。
首先,我們把上面的Foo類增加"invokeMethod"方法:
class Foo
{
def foo()
{
'foo'
}
def invokeMethod(String name,args)
{
'invoke'
}
}
同時,我們在運行期內也增加該方法,然後做測試:
Foo.metaClass.invokeMethod = {
String name,args1 ->
def m = Foo.metaClass.getMetaMethod(name,args1)
def result
if(m)
result = m.invoke(delegate,args1)
else
result = 'test'
result
}
def foo = new Foo()
println foo.foo()
println foo.test()
運行的結果為:
foo
test
從結果可以看出,metaClass的"invokeMethod"方法覆蓋了對象的"invokeMethod"方法。即如果存在metaClass的"invokeMethod"方法,則優先調用metaClass的"invokeMethod"方法,而不管對象的"invokeMethod"方法是否存在。
最後,我們還定義一個名詞:GroovyInterceptable的"invokeMethod"方法。這個名詞其實我們在《Groovy探索之MOP 一 invokeMethod和methodMissing方法》也接觸過,就是一個類如果實現了"GroovyInterceptable"接口,那麼它的類本身的"invokeMethod"方法,就是GroovyInterceptable的"invokeMethod"方法。如下所示:
class Foo implements GroovyInterceptable{
def foo()
{
'foo'
}
def invokeMethod(String name,args)
{
'invoke'
}
}
現在,我們需要知道,如果一個對象的GroovyInterceptable的"invokeMethod"方法和metaClass的"invokeMethod"方法都存在,那麼又是如何調用的?
還是對上面已經實現了"GroovyInterceptable"接口的Foo類如下測試:
Foo.metaClass.invokeMethod = {
String name,args1 ->
def m = Foo.metaClass.getMetaMethod(name,args1)
def result
if(m)
result = m.invoke(delegate,args1)
else
result = 'test'
result
}
def foo = new Foo()
println foo.foo()
println foo.test()
運行結果為:
invoke
invoke
從運行結果可以看出,GroovyInterceptable的"invokeMethod"方法是優先於metaClass的"invokeMethod"方法的調用的;或者說,如果一個對象同時存在GroovyInterceptable的"invokeMethod"方法和metaClass的"invokeMethod"方法,那麼系統將調用GroovyInterceptable的"invokeMethod"方法,而不管metaClass的"invokeMethod"方法。
最後,我們來做一個總結吧。對於一個對象的方法調用,如果該對象的類實現了"GroovyInterceptable"接口,那麼,系統將調用GroovyInterceptable的"invokeMethod"方法;否則,系統將調用metaClass的"invokeMethod"方法;如果該對象沒有實現metaClass的"invokeMethod"方法,那麼系統將調用對象的"invokeMethod"方法。