Groovy 對(duì)象
Groovy 中的對(duì)象其實(shí)本質(zhì)也是 Java 對(duì)象灾票,只不過(guò)比 Java 對(duì)象附加了一些其它的功能。在 Groovy 中的對(duì)象茫虽,其頂級(jí)父類也是 java.lang.Object
刊苍,同時(shí)其也實(shí)現(xiàn)了 groovy.lang.GroovyObject
接口。
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);
}
我們的 Groovy 中的對(duì)象是可以直接調(diào)用 GroovyObject 接口中的方法濒析,然而我們定義的 Groovy 類中沒(méi)有自己實(shí)現(xiàn)過(guò)該接口中的任何方法正什,因此其必定有一個(gè)類幫我們實(shí)現(xiàn)好了這些方法。現(xiàn)在還未得到證實(shí)号杏,但是我猜測(cè)是 GroovyObjectSupport 這個(gè)類提供的實(shí)現(xiàn)婴氮,并且在運(yùn)行時(shí)注入到了我們的 Groovy 類中,所以我們才能調(diào)用這些方法盾致。
public abstract class GroovyObjectSupport implements GroovyObject {
// never persist the MetaClass
private transient MetaClass metaClass;
public GroovyObjectSupport() {
this.metaClass = InvokerHelper.getMetaClass(this.getClass());
}
public Object getProperty(String property) {
return getMetaClass().getProperty(this, property);
}
public void setProperty(String property, Object newValue) {
getMetaClass().setProperty(this, property, newValue);
}
public Object invokeMethod(String name, Object args) {
return getMetaClass().invokeMethod(this, name, args);
}
public MetaClass getMetaClass() {
if (metaClass == null) {
metaClass = InvokerHelper.getMetaClass(getClass());
}
return metaClass;
}
public void setMetaClass(MetaClass metaClass) {
this.metaClass = metaClass;
}
}
這里重點(diǎn)關(guān)注一個(gè)對(duì)象 MetaClass主经。在 Groovy 的世界中,每個(gè)對(duì)象(不管是 Groovy 對(duì)象還是 Java 對(duì)象)都包含一個(gè) MetaClass 對(duì)象庭惜,該 MetaClass 對(duì)象持有其所依附的對(duì)象的所有信息(包括屬性和方法)罩驻,每當(dāng)我們調(diào)用一個(gè)對(duì)象的方法時(shí),都是由該 MetaClass 對(duì)象負(fù)責(zé)路由對(duì)方法的調(diào)用护赊。我們知道一旦一個(gè)類被加載進(jìn) JVM惠遏,那么這個(gè)類就無(wú)法修改了,但是我們可以修改這個(gè)類的 MetaClass 對(duì)象百揭,從而實(shí)現(xiàn)對(duì)類動(dòng)態(tài)的添加方法和行為爽哎。
Groovy 中還有一種特殊的對(duì)象——實(shí)現(xiàn)了 GroovyInterceptable 接口的類,GroovyInterceptable 接口是一個(gè)標(biāo)記接口器一,其擴(kuò)展了 GroovyObject课锌,對(duì)于實(shí)現(xiàn)了該接口的對(duì)象而言,只要調(diào)用該對(duì)象上的任何方法祈秕,都會(huì)被 invokeMethod 方法攔截渺贤。(要實(shí)現(xiàn)攔截,不僅要在類的定義中聲明實(shí)現(xiàn)該接口请毛,同時(shí)還要重寫(xiě)其 invokeMethod 方法志鞍,才會(huì)有攔截的效果)
public interface GroovyInterceptable extends GroovyObject {
}
Groovy 中的方法調(diào)用機(jī)制
該圖描述了 Groovy 中方法調(diào)用的路由機(jī)制。這里做以下補(bǔ)充:
- invokeMethod 方法是 GroovyObject 接口中的方法方仿,所有的 Groovy 類都默認(rèn)實(shí)現(xiàn)了該方法固棚。而 GroovyInterceptable 只是一個(gè)標(biāo)記接口统翩,該接口的作用是將 invokeMethod 方法的調(diào)用時(shí)機(jī)提前到了最前面,也就是所有的方法調(diào)用都會(huì)先統(tǒng)一路由到 invokeMethod 方法中此洲,若為實(shí)現(xiàn) GroovyInterceptable 接口厂汗,那么 invokeMethod 方法只有最后才有機(jī)會(huì)執(zhí)行。
- 若在類的定義中聲明了 GroovyInterceptable 接口呜师,但是在類中沒(méi)有覆蓋 invokeMethod 方法娶桦,則等同于沒(méi)有實(shí)現(xiàn) GroovyInterceptable 接口,路由轉(zhuǎn)向左側(cè)汁汗。
- 若未實(shí)現(xiàn) GroovyInterceptable 接口衷畦,而一個(gè)類的外部直接調(diào)用了 invokeMethod 方法,那么就是方法的直接調(diào)用了知牌,不存在攔不攔截的問(wèn)題祈争,但是如果該類中又沒(méi)有覆蓋 invokeMethod 方法,那么會(huì)調(diào)用 methodMissing 方法(如果有的話)
- 若向一個(gè)類的 metaClass 中添加了 invokeMethod 方法或者 methodMissing 方法角寸,在外部調(diào)用一個(gè)不存在的方法時(shí)铛嘱,會(huì)路由到該 invokeMethod 方法上,如果沒(méi)有實(shí)現(xiàn) invokeMethod 方法袭厂,那么會(huì)路由到 metaClass 上的 methodMissing 方法上(如果有的話)
提供一個(gè)例子直觀的感受下 groovy 的方法路由機(jī)制
class TestMethodInvocation extends GroovyTestCase {
void testInterceptedMethodCallonPOJO() {
def val = new Integer(3)
Integer.metaClass.toString = { -> 'intercepted' }
assertEquals "intercepted", val.toString()
}
//實(shí)現(xiàn)了 GroovyInterceptable 接口,復(fù)寫(xiě) invokeMethod,那么所有的方法調(diào)用,都會(huì)被路由到 invokeMethod 中
void testInterceptableCalled() {
def obj = new AnInterceptable()
assertEquals 'intercepted', obj.existingMethod()
assertEquals 'intercepted', obj.nonExistingMethod()
assertEquals 'intercepted', obj.invokeMethod("existingMethod", null)
assertEquals 'intercepted', obj.invokeMethod("nonExistingMethod", null)
}
void testInterceptedExistingMethodCalled() {
//將原有的 ex2 方法覆蓋了
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()
assertEquals 'invoke called', obj.invokeMethod("haha", null)
}
void testInvokeMethodCalledForOnlyNonExistent() {
def obj = new ClassWithInvokeOnly()
assertEquals 'existingMethod', obj.existingMethod()
assertEquals 'invoke called', obj.nonExistingMethod()
}
void testClassWithMethodMissingOnly(){
def obj = new ClassWithMethodMissingOnly()
assertEquals 'existingMethod', obj.existingMethod()
assertEquals 'missing called', obj.nonExistingMethod()
assertEquals 'missing called', obj.invokeMethod("haha",null)
}
void testMethodFailsOnNonExistent() {
def obj = new TestMethodInvocation()
shouldFail(MissingMethodException) { obj.nonExistingMethod() }
}
}
// 實(shí)現(xiàn)了 GroovyInterceptable 接口,復(fù)寫(xiě) invokeMethod,那么所有的方法調(diào)用,都會(huì)被路由到 invokeMethod 中
class AnInterceptable implements GroovyInterceptable {
def existingMethod() {}
def invokeMethod(String name, args) { 'intercepted' }
}
// 普通的 Groovy 類
class AGroovyObject {
def existingMethod() { 'existingMethod' }
def existingMethod2() { 'existingMethod2' }
def closureProp = { 'closure called' }
}
// 普通的 Groovy 類,并實(shí)現(xiàn) invokeMethod 和 methodMissing 方法
class ClassWithInvokeAndMissingMethod {
def existingMethod() { 'existingMethod' }
def invokeMethod(String name, args) { 'invoke called' }
def methodMissing(String name, args) { 'missing called' }
}
// 普通的 Groovy 類,并實(shí)現(xiàn) invokeMethod
class ClassWithInvokeOnly {
def existingMethod() { 'existingMethod' }
def invokeMethod(String name, args) { 'invoke called' }
}
class ClassWithMethodMissingOnly {
def existingMethod() { 'existingMethod' }
def methodMissing(String name, args) { 'missing called' }
}
MOP(MetaObject Protocol)
MOP:元對(duì)象協(xié)議。由 Groovy 語(yǔ)言中的一種協(xié)議球匕。該協(xié)議的出現(xiàn)為元編程提供了優(yōu)雅的解決方案纹磺。而 MOP 機(jī)制的核心就是 MetaClass。
元編程:編寫(xiě)能夠操作程序的程序亮曹,也包括操作程序自身橄杨。
正是 Groovy 提供了 MOP 的機(jī)制,才使得 Groovy 對(duì)象更加靈活照卦,我們可以根據(jù)對(duì)象的 metaClass式矫,動(dòng)態(tài)的查詢對(duì)象的方法和屬性。這里所屬的動(dòng)態(tài)指的是在運(yùn)行時(shí)役耕,根據(jù)所提供的方法或者屬性的字符串采转,即可得到
有點(diǎn)類似于 Java 中的反射,但是在使用上卻比 Java 中的反射簡(jiǎn)單的多瞬痘。
動(dòng)態(tài)訪問(wèn)方法
常用的方法有:
- getMetaMethod()
- resondsTo()
- hasProperty()
- ......
在使用了 getMetaMethod 方法得到一個(gè) MetaMethod 后故慈,可以調(diào)用其 invoke 方法執(zhí)行。
str = 'hello'
targetMethod = str.metaClass.getMetaMethod('toUpperCase')
targetMethod.invoke(str)
動(dòng)態(tài)訪問(wèn)方法或?qū)傩浴狦roovy 的語(yǔ)法糖
class Foo{
int bar
def func1(){}
}
foo = new Foo()
String propertyName = 'bar'
String methodName = 'func1'
//訪問(wèn)屬性
foo[propertyName]
foo."$propertyName"
//訪問(wèn)方法
foo."$methodName"()
foo.invokeMethod(methodName,null)