在一个Groovy应用中,会调用三种对象:
- POJO 普通java对象
extends java.lang.Object - POGO 普通Groovy对象
extends java.lang.Object
implements groovy.lang.GroovyObject - Groovy拦截器
extends java.lang.Object
implements groovy.lang.GroovyInterceptable
[注意:interface groovy.lang.GroovyInterceptable extend groovy.lang.GroovyObject][GroovyInterceptable中没有抽象方法,它只是起标记作用的接口]
而在Groovy中,动态地注入方法、调用方法、属性就是使用元类metaClass来完成的(类似于Java的反射机制),请求的方法会被委托到这个类。
- 对于POJO而言,它是一个普通的java对象,甚至有可能是一个定义在jdk中的类,原生不可能有metaClass。为了实现这一功能,Groovy维护一个名为“MetaClassRegistry”的Map类对象,对于每一个POJO都能找到其对应的metaClass。
在POJO的metaClass定义的方法和拦截器都优先于POJO中定义的方法
考虑下面的例子,有一个java类:
下面在Groovy脚本文件中有如下代码:public class Dog { public void bark(){ System.out.println("Wow"); } }
上述代码输出“don't bark!”,而不是"Wow"Dog.metaClass.bark={->'don't bark!'} println new Dog().bark()
- 对于一个拦截器对象,在其上调用的所有方法都会被代理到invokeMethod上,无论存不存在。
上述定义了一个拦截器MyInterceptor,然后在拦截器实例上调用了一个存在的方法introduceLang以及不存在的方法unknownMethod,但是它们的输出都是“Hello! Welcome to use MyInterceptor.”。即无论方法存在与否,拦截器都会路由到invokeMethod上。class MyInterceptor implements groovy.lang.GroovyInterceptable{ def introduceLang(){ "Groovy" } def invokeMethod(String name,args){ //注意invokeMethod的签名 "Hello! Welcome to use MyInterceptor." } } println(new MyInterceptor().introduceLang()) println(new MyInterceptor().unknownMethod())
- 如果是POGO对象,它内部就有一个metaClass的属性,指向它的元类。在上面调用方法的路由规则比较复杂:
由于拦截器是特殊的POGO,所以第一步先判断是否是拦截器,如果是就按照第2条路由;否则看看方法在不在元类中,如果在就直接调用(即元类方法优先级高于自身方法),不然就看是否在类中;否则看是否存在同名的闭包属性,如果有则调用闭包;否则看看是否存在一个特殊的方法methodMissing方法,如果有就调用它;否则看看有没有invokeMethod方法(默认实现是抛出异常),如果有则调用,如果没有就抛出异常。
下面是一些说明,其中的类都是POGO(且不是拦截器):-
路由到达最后一步,因为它存在invokeMethod,所以被调用了。class ClassWithInvokeOnly{ def invokeMethod(String name,args){'invoke called'} } def obj=new ClassWithInvokeOnly(); assert 'invoke called'==obj.unknownMethod(); //断言为真
-
路由到达倒数第二步,因为它存在methodMissing,所以被调用。class ClassWithInvokeOnly{ def methodMissing(String name,args){ //注意方法签名 'missing called' } } def obj=new ClassWithInvokeOnly(); assert 'missing called'==obj.unknownMethod(); //断言为真
-
调用到倒数第三步,因为有同名闭包属性,所以被调用。class ClassWithInvokeOnly{ def closure={ 'closureString' } } def obj=new ClassWithInvokeOnly(); assert 'closureString'==obj.closure();//断言为真
-
路由到第二步,元类中的方法会覆盖类中原本的同名方法。class ClassWithInvokeOnly{ def sayHello(){ 'hello' } } ClassWithInvokeOnly.metaClass.sayHello={ 'metaClass hello' } def obj=new ClassWithInvokeOnly(); assert 'metaClass hello'==obj.sayHello(); //断言为真
但是考虑下面的情况:
上述情况总,因为元类中方法没有有参数的方法,但是类中本身有带参数的同名方法,所以会调用。实际上,在发现元类中不存在这个方法后就会在本类中寻找是否有该方法,如果有就调用,然后找到了,程序于是就终止了。class ClassWithInvokeOnly{ def sayHello(name){ 'hello '+name } } ClassWithInvokeOnly.metaClass.sayHello={ 'metaClass hello' } def obj=new ClassWithInvokeOnly(); assert 'hello Bob'==obj.sayHello(“Bob”); //断言为真
-