Groovy探索之MOP 十一 运行期内覆盖invokeMethod
我们很早就会使用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"方法。