groovy.lang.GroovyObject是Groovy中的主接口,就像Java中的Object。在 groovy.lang.GroovyObjectSupportGroovyObject中有默认的实现,并且负责将调用转移到groovy.lang.MetaClass 上去。GroovyObject的源码如下。

  1. package groovy.lang;
  2. public interface GroovyObject {
  3. Object invokeMethod(String name, Object args);
  4. Object getProperty(String propertyName);
  5. void setProperty(String propertyName, Object newValue);
  6. MetaClass getMetaClass();
  7. void setMetaClass(MetaClass metaClass);
  8. }

1.invokeMethod

此方法主要与GroovyInterceptable 接口或对象的MetaClass结合使用,从而拦截所有方法的调用。
当调用的方法不存在于 Groovy 对象上时,也会调用它。这是一个使用覆盖invokeMethod()方法的简单示例:

  1. class SomeGroovyClass {
  2. def invokeMethod(String name, Object args) {
  3. return "called invokeMethod $name $args"
  4. }
  5. def test() {
  6. return 'method exists'
  7. }
  8. }
  9. def someGroovyClass = new SomeGroovyClass()
  10. assert someGroovyClass.test() == 'method exists'
  11. assert someGroovyClass.someMethod() == 'called invokeMethod someMethod []'

然而,不鼓励使用invokeMethod拦截缺失的方法。如果目的是在方法调度失败的情况下仅截获方法调用,请改用methodMissing

2.get/setProperty

通过重写当前对象的getProperty()方法。可以拦截每一次对属性的读取访问。
以下是一个简单的例子:

  1. class SomeGroovyClass {
  2. def property1 = 'ha'
  3. def field2 = 'ho'
  4. def field4 = 'hu'
  5. def getField1() {
  6. return 'getHa'
  7. }
  8. def getProperty(String name) {
  9. // 将请求转发给除 field3 之外的所有属性的 getter
  10. if (name != 'field3')
  11. return metaClass.getProperty(this, name)
  12. else
  13. return 'field3'
  14. }
  15. }
  16. def someGroovyClass = new SomeGroovyClass()
  17. assert someGroovyClass.field1 == 'getHa'
  18. assert someGroovyClass.field2 == 'ho'
  19. assert someGroovyClass.field3 == 'field3'
  20. 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'