Groovy中的MOP——Groovy对象

Groovy对象是带有附加功能的Java对象。

POJO:普通的Java对象

POGO:用Groovy编写的对象,扩展了java.lang.Object,同时实现了groovy.lang.GroovyObject。

Groovy拦截器:扩展了GroovyInterceptable的Groovy对象。

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);
}

invokeMethod()、getProperty()和setProperty()使Java对象具有了高度的多态性,可以使用它们来处理运行中的方法和属性。getMetaClass()和setMetaClass()使创建代理拦截POGO的方法调用、在POGO上注入方法变得非常容易。

public interface GroovyInterceptable extends GroovyObject {
}

GroovyInterceptable接口扩展了GroovyObject接口,对于实现该接口的对象,其上所有的方法调用,都会被它的invokeMethod()方法拦截。

Groovy支持对POJO和POGO进行编程。对于POJO,Groovy维护了MetaClass的一个MetaClassRegistry;POGO有一个到其MetaClass的直接引用。

当我们调用一个方法时,Groovy会检查目标对象是一个POJO还是一个POGO。不同的对象类型,Groovy处理的方式不一样。

Groovy如何处理POGO上的方法调用
package exploring


class TestMethodInvocation extends GroovyTestCase{


    void testInterceptedMethodCallOnPOJO(){
        def val = new Integer(3)
        Integer.metaClass.toString = {->'intercepted'}

        assertEquals "intercepted",val.toString()
    }

    void testInterceptableCalled(){
        def obj = new AnInterceptable()
        assertEquals "intercepted",obj.existingMethod()
        assertEquals "intercepted",obj.nonExistingMethod()
    }

    void testInterceptedExistingMethodCalled(){
        AGroovyObject.metaClass.existingMethod2 = {-> 'intercepted'}
        def obj = new AGroovyObject()
        assertEquals "intercepted",obj.existingMethod2()
    }

    void testUnInterceptedExistingMethodCalled(){
        def obj = new AGroovyObject()
        assertEquals "existingMethod",obj.existingMethod()
    }

    void testPropertyThatIsClosureCalled(){
        def obj = new AGroovyObject()
        assertEquals 'closure called',obj.closureProp()
    }

    void testMethodMissingCalledOnlyForNonExistent(){
        def obj = new ClassWithInvokeAndMissingMethod()
        assertEquals "existingMethod",obj.existingMethod()
        assertEquals "missing called",obj.nonExistingMethod()
    }

    def testInvokeMethodCalledForOnlyNonExistent(){
        def obj = new ClassWithInvokeOnly()
        assertEquals "existingMethod",obj.existingMethod()
        assertEquals "invoke called",obj.nonExistingMethod()
    }

    def testMethodFailsONNonExistent(){
        def obj = new TestMethodInvocation()
        shouldFail (MissingMethodException){
            obj.nonExistingMethod()
        }
    }

}

class AnInterceptable implements GroovyInterceptable{
    def existingMethod(){}
    def invokeMethod(String name,args){
        'intercepted'
    }
}

class AGroovyObject{
    def existingMethod(){'existingMethod'}
    def existingMethod2(){'existingMethod2'}
    def closureProp = {'closure called'}
}

class ClassWithInvokeAndMissingMethod{
    def existingMethod(){'existingMethod'}
    def invokeMethod(String name,args){'invoke called'}
    def methodMissing(String name,args){'missing called'}
}

class ClassWithInvokeOnly{
    def existingMethod(){'existingMethod'}
    def invokeMethod(String name,args){'invoke called'}
}

运行时,可以查询一个对象的方法和属性,以确定该对象是否支持某一特定行为。

  • 可以使用MetaObjectProtocal的getMetaMethod()(MetaClass扩展了MetaObjectProtocal)来获得一个元方法。
  • 如果要查找一个static方法,可以使用getStaticMetaMethod()。
  • 要获得重载方法的列表,使用这些方法的复数形式——getMetaMethods()和getStaticMetaMethods()。
  • getProperty()和getStaticProperty()可以获得元属性。
  • 使用respondsTo()检查方法,使用hasProperty()检查属性。

MetaMethod的使用

str = "hello"
methodName = "toUpperCase"
methodOfInterest = str.metaClass.getMetaMethod(methodName)
println methodOfInterest.invoke(str)


print "Does String respond to toUpperCase()? "
println String.metaClass.respondsTo(str, 'toUpperCase') ? 'yes' : 'no'

print "Does String respond to compareTo(String)? "
println String.metaClass.respondsTo(str, "compareTo", 'test') ? 'yes' : 'no'

print "Does String respond to toUpperCase(int)? "
println String.metaClass.respondsTo(str, "toUpperCase", 5) ? 'yes' : 'no'

动态访问对象

def printInfo(obj){

    usrRequestedProperty = 'bytes'
    usrRequestedMethod = 'toUpperCase'

    println obj[usrRequestedProperty]
    println obj."$usrRequestedProperty"

    println obj."$usrRequestedMethod"()
    println obj.invokeMethod(usrRequestedMethod,null)

}
printInfo("hello")

要调用一个属性,可以使用索引操作符[],或使用点记号后跟一个计算属性名的GString。

迭代一个对象的所有属性:

println "Properties of 'hello are:"
'hello'.properties.each{ println it}