groovy.lang.GroovyObject是Groovy中的主接口,就像Java中的Object。在 groovy.lang.GroovyObjectSupport 中GroovyObject中有默认的实现,并且负责将调用转移到groovy.lang.MetaClass 上去。GroovyObject的源码如下。
package groovy.lang;public interface GroovyObject {Object invokeMethod(String name, Object args);Object getProperty(String propertyName);void setProperty(String propertyName, Object newValue);MetaClass getMetaClass();void setMetaClass(MetaClass metaClass);}
1.invokeMethod
此方法主要与GroovyInterceptable 接口或对象的MetaClass结合使用,从而拦截所有方法的调用。
当调用的方法不存在于 Groovy 对象上时,也会调用它。这是一个使用覆盖invokeMethod()方法的简单示例:
class SomeGroovyClass {def invokeMethod(String name, Object args) {return "called invokeMethod $name $args"}def test() {return 'method exists'}}def someGroovyClass = new SomeGroovyClass()assert someGroovyClass.test() == 'method exists'assert someGroovyClass.someMethod() == 'called invokeMethod someMethod []'
然而,不鼓励使用invokeMethod拦截缺失的方法。如果目的是在方法调度失败的情况下仅截获方法调用,请改用methodMissing。
2.get/setProperty
通过重写当前对象的getProperty()方法。可以拦截每一次对属性的读取访问。
以下是一个简单的例子:
class SomeGroovyClass {def property1 = 'ha'def field2 = 'ho'def field4 = 'hu'def getField1() {return 'getHa'}def getProperty(String name) {// 将请求转发给除 field3 之外的所有属性的 getterif (name != 'field3')return metaClass.getProperty(this, name)elsereturn 'field3'}}def someGroovyClass = new SomeGroovyClass()assert someGroovyClass.field1 == 'getHa'assert someGroovyClass.field2 == 'ho'assert someGroovyClass.field3 == 'field3'assert someGroovyClass.field4 == 'hu'
您可以通过覆盖setProperty()方法来拦截对属性的写访问:
class POGO {
String property
void setProperty(String name, Object value) {
this.@"$name" = 'overridden'
}
}
def pogo = new POGO()
pogo.property = 'a'
assert pogo.property == 'overridden'
3.get/setMetaClass
您可以访问对象的metaClass 或设置自己的MetaClass实现来更改默认的拦截机制。例如,您可以编写自己的MetaClass接口实现,并将其分配给对象,以更改拦截机制:
// getMetaclass
someObject.metaClass
// setMetaClass
someObject.metaClass = new OwnMetaClassImplementation()
:::info 您可以在GroovyInterceptable 中找到其他示例。 :::
4.get/setAttribute
此功能与MetaClass实现有关。在默认实现中,您可以访问字段而不调用它们的 getter 和 setter。下面的示例演示了这种方法:
class SomeGroovyClass {
def field1 = 'ha'
def field2 = 'ho'
def getField1() {
return 'getHa'
}
}
def someGroovyClass = new SomeGroovyClass()
assert someGroovyClass.metaClass.getAttribute(someGroovyClass, 'field1') == 'ha'
assert someGroovyClass.metaClass.getAttribute(someGroovyClass, 'field2') == 'ho'
lass POGO {
private String field
String property1
void setProperty1(String property1) {
this.property1 = "setProperty1"
}
}
def pogo = new POGO()
pogo.metaClass.setAttribute(pogo, 'field', 'ha')
pogo.metaClass.setAttribute(pogo, 'property1', 'ho')
assert pogo.field == 'ha'
assert pogo.property1 == 'ho'
5.methodMissing
Groovy 支持methodMissing. 此方法与invokeMethod的不同之处在于,它仅在无法找到给定名称和/或给定参数的方法时在方法分派失败的情况下调用:
class Foo {
def methodMissing(String name, def args) {
return "this is me"
}
}
assert new Foo().someUnknownMethod(42l) == 'this is me'
通常在使用methodMissing时可以缓存结果以供下次调用相同的方法时使用。
比如,参考GORM中的动态查找,这些是用methodMissing实现的。代码类似于这样:
class GORM {
def dynamicMethods = [...] // an array of dynamic methods that use regex
def methodMissing(String name, args) {
def method = dynamicMethods.find { it.match(name) }
if(method) {
GORM.metaClass."$name" = { Object[] varArgs ->
method.invoke(delegate, name, varArgs)
}
return method.invoke(delegate,name, args)
}
else throw new MissingMethodException(name, delegate, args)
}
}
请注意,如果我们找到一个要调用的方法,我们将使用ExpandoMetaClass动态注册一个新方法。这样,下次调用同一方法时,效率更高。这种使用methodMissing的方法没有invokeMethod的开销,并且从第二次调用开始并不昂贵。
6.propertyMissing
Groovy支持propertyMissing的概念,用于拦截其他失败的属性解析尝试。对于getter方法,propertyMissing采用包含属性名称的单个String参数:
class Foo {
def propertyMissing(String name) { name }
}
assert new Foo().boo == 'boo'
只有当Groovy运行时找不到给定属性的getter方法时,才会调用propertyMissing(String)方法。
对于setter方法,可以添加第二个propertyMissing参数,该参数保存传入的值:
class Foo {
def storage = [:]
def propertyMissing(String name, value) { storage[name] = value }
def propertyMissing(String name) { storage[name] }
}
def f = new Foo()
f.foo = "bar"
assert f.foo == "bar"
最佳实践是在methodMissing运行时动态注册新属性以提高整体查找性能。
7.static methodMissing
methodMissing方法的静态变体可以通过ExpandoMetaClass添加,也可以使用$static_methodMissing方法在类级别实现。
class Foo {
static def $static_methodMissing(String name, Object args) {
return "Missing static method name is $name"
}
}
assert Foo.bar() == 'Missing static method name is bar'
8.static propertyMissing
propertyMissing方法的静态变体可以通过ExpandoMetaClass添加,也可以使用$static_propertyMissing方法在类级别实现。
class Foo {
static def $static_propertyMissing(String name) {
return "Missing static property name is $name"
}
}
assert Foo.foobar == 'Missing static property name is foobar'
