Groovy 對(duì)象和 MOP

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ī)制

image.png

該圖描述了 Groovy 中方法調(diào)用的路由機(jī)制。這里做以下補(bǔ)充:

  1. 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í)行。
  2. 若在類的定義中聲明了 GroovyInterceptable 接口呜师,但是在類中沒(méi)有覆蓋 invokeMethod 方法娶桦,則等同于沒(méi)有實(shí)現(xiàn) GroovyInterceptable 接口,路由轉(zhuǎn)向左側(cè)汁汗。
  3. 若未實(shí)現(xiàn) GroovyInterceptable 接口衷畦,而一個(gè)類的外部直接調(diào)用了 invokeMethod 方法,那么就是方法的直接調(diào)用了知牌,不存在攔不攔截的問(wèn)題祈争,但是如果該類中又沒(méi)有覆蓋 invokeMethod 方法,那么會(huì)調(diào)用 methodMissing 方法(如果有的話)
  4. 若向一個(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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末框全,一起剝皮案震驚了整個(gè)濱河市察绷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌津辩,老刑警劉巖拆撼,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件容劳,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡闸度,警方通過(guò)查閱死者的電腦和手機(jī)竭贩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)筋岛,“玉大人娶视,你說(shuō)我怎么就攤上這事≌鲈祝” “怎么了肪获?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)柒傻。 經(jīng)常有香客問(wèn)我孝赫,道長(zhǎng),這世上最難降的妖魔是什么红符? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任青柄,我火速辦了婚禮,結(jié)果婚禮上预侯,老公的妹妹穿的比我還像新娘致开。我一直安慰自己,他們只是感情好萎馅,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布双戳。 她就那樣靜靜地躺著,像睡著了一般糜芳。 火紅的嫁衣襯著肌膚如雪飒货。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天峭竣,我揣著相機(jī)與錄音塘辅,去河邊找鬼。 笑死皆撩,一個(gè)胖子當(dāng)著我的面吹牛扣墩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播毅访,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼沮榜,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了喻粹?” 一聲冷哼從身側(cè)響起蟆融,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎守呜,沒(méi)想到半個(gè)月后型酥,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體山憨,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年弥喉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了郁竟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡由境,死狀恐怖棚亩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情虏杰,我是刑警寧澤讥蟆,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站纺阔,受9級(jí)特大地震影響瘸彤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜笛钝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一质况、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧玻靡,春花似錦结榄、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至最蕾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間老厌,已是汗流浹背瘟则。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留枝秤,地道東北人醋拧。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像淀弹,于是被迫代替她去往敵國(guó)和親丹壕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,748評(píng)論 6 342
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理薇溃,服務(wù)發(fā)現(xiàn)菌赖,斷路器,智...
    卡卡羅2017閱讀 134,601評(píng)論 18 139
  • Groovy學(xué)習(xí)目錄-傳送門(mén) 元編程(Metaprogramming)->百度百科 Groovy語(yǔ)言支持兩種類型的...
    化作春泥_閱讀 9,038評(píng)論 0 19
  • groovy是什么 Groovy 是下一代的Java語(yǔ)言沐序,跟java一樣,它也運(yùn)行在 JVM 中琉用。 作為跑在JVM...
    ronaldo18閱讀 674評(píng)論 0 4
  • 一句話總結(jié):小狗錢(qián)錢(qián)是一本值得一讀的好書(shū)堕绩,一本我愿意推薦給我的孩子讀的入門(mén)理財(cái)書(shū)。越早讀邑时,越好奴紧。 以下是幾個(gè)值得記...
    AriseLu閱讀 229評(píng)論 0 0