在一個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中,動態(tài)地注入方法、調用方法、屬性就是使用元類metaClass來完成的(類似于Java的反射機制),請求的方法會被委托到這個類。
- 對于POJO而言,它是一個普通的java對象,甚至有可能是一個定義在jdk中的類,原生不可能有metaClass。為了實現(xiàn)這一功能,Groovy維護一個名為“MetaClassRegistry”的Map類對象,對于每一個POJO都能找到其對應的metaClass。
在POJO的metaClass定義的方法和攔截器都優(yōu)先于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對象,它內(nèi)部就有一個metaClass的屬性,指向它的元類。在上面調用方法的路由規(guī)則比較復雜:
POGO上的方法調用
由于攔截器是特殊的POGO,所以第一步先判斷是否是攔截器,如果是就按照第2條路由;否則看看方法在不在元類中,如果在就直接調用(即元類方法優(yōu)先級高于自身方法),不然就看是否在類中;否則看是否存在同名的閉包屬性,如果有則調用閉包;否則看看是否存在一個特殊的方法methodMissing方法,如果有就調用它;否則看看有沒有invokeMethod方法(默認實現(xiàn)是拋出異常),如果有則調用,如果沒有就拋出異常。
下面是一些說明,其中的類都是POGO(且不是攔截器):-
路由到達最后一步,因為它存在invokeMethod,所以被調用了。class ClassWithInvokeOnly{ def invokeMethod(String name,args){'invoke called'} } def obj=new ClassWithInvokeOnly(); assert 'invoke called'==obj.unknownMethod(); //斷言為真 -
路由到達倒數(shù)第二步,因為它存在methodMissing,所以被調用。class ClassWithInvokeOnly{ def methodMissing(String name,args){ //注意方法簽名 'missing called' } } def obj=new ClassWithInvokeOnly(); assert 'missing called'==obj.unknownMethod(); //斷言為真 -
調用到倒數(shù)第三步,因為有同名閉包屬性,所以被調用。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(); //斷言為真
但是考慮下面的情況:
上述情況總,因為元類中方法沒有有參數(shù)的方法,但是類中本身有帶參數(shù)的同名方法,所以會調用。實際上,在發(fā)現(xiàn)元類中不存在這個方法后就會在本類中尋找是否有該方法,如果有就調用,然后找到了,程序于是就終止了。class ClassWithInvokeOnly{ def sayHello(name){ 'hello '+name } } ClassWithInvokeOnly.metaClass.sayHello={ 'metaClass hello' } def obj=new ClassWithInvokeOnly(); assert 'hello Bob'==obj.sayHello(“Bob”); //斷言為真
-
