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 之外的所有属性的 getter
if (name != 'field3')
return metaClass.getProperty(this, name)
else
return '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'