加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Groovy基础——MetaClass详解

发布时间:2020-12-14 16:45:35 所属栏目:大数据 来源:网络整理
导读:这篇文章将对Groovy的其中一个核心内容MetaClass(MOP)讲解。由于该部分内容较多。所以还是利用一个个例子逐步的阐述。 ? 一、拦截方法调用和参数获取 示例1: ? Java代码?? class ?MyClass{?? ????def?hello(){?? ???????? 'invoked?hello?directly' ?? ????

这篇文章将对Groovy的其中一个核心内容MetaClass(MOP)讲解。由于该部分内容较多。所以还是利用一个个例子逐步的阐述。

?

一、拦截方法调用和参数获取

示例1:

?

Java代码??

收藏代码

  1. class?MyClass{??
  2. ????def?hello(){??
  3. ????????'invoked?hello?directly'??
  4. ????}??
  5. ????def?invokeMethod(String?name,?Object?args){??
  6. ????????return?"unknown?method?$name(${args.join(',?')})"??
  7. }??
  8. def?mine=?new?MyClass()??
  9. assert?mine.hello()?==?'invoked?hello?directly'??
  10. assert?mine.foo("Mark",?19)?==?'unknown?method?foo(Mark,?19)'??
?

首先我们在groovy脚本中定义了一个Myclass对象,在groovy中任何的对象都是实现GroovyObject并且继承GroovyObjectSupport的,在GroovyObject的接口中,我们可以看到几个方法首先是getMetaClass方法和setMetaClass方法,metaClass用来支持动态方法和动态参数的调用。另一组方法是getProperty和setProperty方法,这组方法是用来支持动态参数的设定与赋值的。最后还有一个invokeMethod方法,该方法则是用于调用动态方法的。在了解了上述概念后,我们可以理解为MyClass的invokeMethod方法覆盖了GroovyObjectSupport中对应的方法,所以调用未预先定义的foo方法就会进入invokeMethod的实现。而hello方法预先定义则照旧。

示例2:

class?MyClass?implements?GroovyInterceptable{??

  • ????????'invoked?hello()?directly'??
  • ????????"invoked?method?$name(${args.join(',250)"> ??
  • def?mine?=?assert?mine.hello()?==?'invoked?method?hello()'??
  • assert?mine.foo('Mark',19)?==?'invoked?method?foo(Mark,?19)'??
  • assert?mine.&hello()?==?'invoked?hello()?directly'??
  • ?该例子和示例1的不同在于实现了一个GroovyInterceptable接口,仔细看下这个接口的描述,可知道实现该接口的类被调用方法时都是默认使用invokeMethod方法,而不管该方法时动态生成或时静态生成;第2点不同是如果要再此情况下调用原有定义号的方法,需要变通的使用'.&'操作符。

    示例3:

    ????def?greeting?=?'accessed?greeting?directly'??

  • ????Object?getProperty(String?property){??
  • ????????"read?from?property?$property"??
  • ????void?setProperty(String?property,?Object?newVlaue){??
  • throw?new?Exception("wrote?to?property?$property")??
  • assert?mine.greeting?==?'read?from?property?greeting'??
  • try{??
  • ????mine.greeting?=?'hi'??
  • }catch(e){??
  • assert?e.message?==?'wrote?to?property?greeting'??
  • assert?mine.@greeting?==?'accessed?greeting?directly'??
  • 该示例描述了属性的获取特性,在示例1中已经描述设置和获取属性的方法时继承来的,这里不做赘述。默认的通过Gpath(见Gpath具体概念)来处理属性的值,都是通过调用getProperty和SetProperty来代劳的。同样的如果你真希望直接访问参数的值,可以变通的使用'.@'操作符来达成。

    通过Gpath来获得属性值。无论该属性在类中是否有,都是不会出错的执行那2个方法。但是对于类中不存在的属性,忌使用'.@'操作符,会抛出MissingFieldException。

    示例4:

    ????def?greeting?=?'accessed?greeting'??

  • ????def?id?='White:?'??
  • ??????
  • ????????????return?this.@id?+?//access?field?directly??
  • ????????????????????'indirectly?'?+??
  • ????????????????????this.@"$property"??
  • ????????}return?"no?such?property?$property"??
  • ????????}??
  • ????def?hello(Object[]?args){"invoked?hello?with?(${args.join(',?')})"}??
  • ????def?id(){'Green:?'}??
  • this.&id()?+?//call?method?directly??
  • this.&"$name"(args)??
  • return?"no?such?method?$name"??
  • ????}?????
  • assert?mine.greeting?==?'White:?indirectly?accessed?greeting'??
  • assert?mine.farewell?==?'no?such?property?farewell'??
  • assert?mine.hello(1,?'b',0)">3)?==?'Green:?indirectly?invoked?hello?with?(1,?b,?3)'??
  • 19)?==?'no?such?method?foo'??
  • 该示例是对示例 2,3的一个合并,同时他告诉我们我们可以通过操作符'.@' 或者'.&'后使用双引号中定义变量的方法来动态的获取参数或者动态的方法。

    二、MetaClass (描述类的类)

    示例5:

    public?class?MyMetaClass?extends?DelegatingMetaClass{??

  • ????MyMetaClass(Class?thisClass){??
  • super(thisClass)??
  • ????Object?invokeMethod(Object?object,?String?methodName,?Object[]?arguments){??
  • ????????"MyMetaClass:?${super.invokeMethod(object,?methodName,?arguments)}"??
  • class?A{??
  • ????def?bark(){'A:?invoked?bark()'}??
  • ????????"A:?missing?$name(${args.join(',250)"> def?amc?=?new?MyMetaClass(A)??
  • amc.initialize()??
  • def?a?=?new?A()??
  • a.metaClass?=?amc??
  • assert?a.bark()?==?'MyMetaClass:?A:?invoked?bark()'??
  • Thread.start?{??
  • assert?new?A().bark()?==?'A:?invoked?bark()'??
  • assert?a.bleet()?==?'A:?missing?bleet()'??
  • ?该示例的代码较长,主要的意思是我们可以通过任意Groovy对象中的metaClass属性来为改变该对象的方法调用的行为。Groovy为我们提供了DelegatingMetaClass 来让我们实现该功能。

    具体的做法是:首先创建一个自定义的MetaClass类继承于DelegatingMetaClass,同时实现构造方法,以及自定义的方法调用行为(以后的内容将介绍,我们不仅可以改变方法行为)。然后我们可以通过自己创建的metaClass子类来包装你想改变行为的目标类。此处为类A。然后再创建目标类实例,对目标类中得metaClass属性进行设置。随后在该实例上的方法调用将会按照该实例的metaClass属性所对应的处理器,进行处理,该例中是再完成本身方法调用后在前面添加MyMetaClass字符串。

    但是值得注意的是,1.对于另外新创建的A实例,如果没有进行metaClass属性赋值将按照原方法定义执行;2.动态定义的

    方法不受metaClass方法行为改变的影响。见该示例的最后一行。3.上述的特性在新线程内同样有效。

    示例6:

    InvokerHelper.instance.metaRegistry.setMetaClass(A,?amc)?? 该示例只是对示例5的补充,我们曾提到,我们可以通过为目标类实例设置metaClass属性来让metaClass的行为对该实例生效。但是如果要在类范围内生效的话,就需要通过上面的代码进行注册。这样注册过后,将对目标类的所有,任何时候创建的示例,都赋予metaClass的行为。

    这个示例还有一点需要注意。试想这么一种情况,先创建了一个目标类实例,再用示例6的语句注册,再创建一个目标类的实例。metaClass的行为将在哪个对象上生效呢。答案是两者都生效。这点非常关键。一旦进行了类范围metaClass注册,那对于已创建和新创建的对象都生效。

    注:对于高版本的groovy InvokerHelper的instance不存在。可以直接调用metaRegistry。

    示例7:

    import?org.codehaus.groovy.runtime.InvokerHelper;??

  • ????MyMetaClass(Class?theClass){??
  • super(theClass)??
  • ????Object?invokeConstructor(Object[]?arguments){??
  • ????????[]??
  • class?A{}??
  • InvokerHelper.metaRegistry.setMetaClass(A,amc)??
  • assert?a.class?==?ArrayList??
  • assert?(a<<1<<2<<3).size()?==?3??
  • 在之前的示例已经略有提过metaClass不仅可以改变预定义的方法行为。在该示例中就以改变构造行为为例。该metaClass将构造行为变成创建一个数组对象。随后的操作是一目了然的。

    在此值得一提的是,invokeConstructor方法时从metaClass的接口MetaObjectProtocol继承过来,其他的方法可能

    是从不同的继承树而来的,所以可以参见DelegatingMetaClass中得方法签名。我们可以发现有很多invokeXXX和getXXX的方法,也就是说我们可以对这些metaClass行为做更改。主要的我们可以看到有getProperty方法invokeConstructor方法,当然我很兴奋的发现还有一个invokeMissingMethod方法,这个方法似乎就是对我们示例5中不能处理动态定义方法的metaClass行为的一个有用的补充了。

    三、ExpandoMetaClass

    下面这个示例内容较多,在给出例子之前,需要先澄清一些细节,你可能会想我们之前人为的为groovyobject设置metaClass来改变类实例的行为,那默认不设置情况下这个metaClass是什么呢? 其实无论是你设置或者没有设置metaClass它的metaClass都是HandleMetaClass类型的,但是区别在于,里面有一个getAdaptee方法返回的是DelegatingMetaClass中的MetaClass类型的delegate属性,默认情况下,这个delegate会事MetaClassImpl类型的,但是一旦你自己设置了目标类实例的metaClass属性那这个delegate属性就是你设置的了。我们下面要将的这个话题,也就是这一章的标题ExpandoMetaClass(MetaClassImpl的一个子类)正是又一个与MetaClassImpl以及自定义的MetaClass平行的delegate只是这个delegate,支持动态创建方法等等的特性。接着我们先引入示例

    示例8:

    ????String?text??

  • ??}??
  • def?a1=?new?A(text:?'aBCdefG')??
  • assert?a1.metaClass.adaptee.class?==?MetaClassImpl??
  • A.metaClass.inSameCase?=?{->?text.toUpperCase()}??
  • //triggers?conversion?of?MetaClass?of?A?to?ExpandoMetaClass??
  • //then?adds?new?instance?method?'inUpperCase'?to?class??
  • //A.metaClass?{??}??
  • //??
  • def?a2?=?new?A(text:'hiJKLmnOp')??
  • assert?a2.metaClass.adaptee.class?==?ExpandoMetaClass??
  • //MetaClass?of?A?changed?for?instances?created?after?conversion?trigger?only??
  • assert?a2.inSameCase()?==?'HIJKLMNOP'??
  • //new?method?not?available??
  • try{?println?a1.inSameCase();}??
  • catch(e){assert?e?in?MissingMethodException}??
  • A.metaClass.inLowerCase?=?{->?text.toLowerCase()}??
  • assert?a2.inLowerCase()?==?'hijklmnop'??
  • //replace?the?method?definition?with?another??
  • A.metaClass.inSameCase?=?{->?text.toLowerCase()}??
  • assert?a2.inSameCase()?==?'hijklmnop'??
  • //add?static?methods??
  • A.metaClass.'static'.inSameCase?=?{it.toLowerCase()}??
  • assert?A.inSameCase('qRStuVwXyz')?==?'qrstuvwxyz'??
  • ?代码的前几行印证了,默认的delegate(即HandleMetaClass中得metaClass属性)是MetaClassImpl。然后我们调用了A.metaClass { }方法(该方法位于DefaultGroovyMethods中)使得返回的HandleMetaClass中得delegate是

    ExpandoMetaClass类型的。这样我们就能够利用该类型的metaClass为我们做事了。同样的

    A.metaClass.inSameCase = {-> text.toUpperCase()}方法只是在注册metaClass为ExpandoMetaClass的同时

    为其动态添加一个实例方法。同时我们可以在最后几行的代码中获知,动态添加静态方法也是可行的。

    几点需要注意的:1.只有在切换delegate为ExpandoMetaClass后创建的目标对象才能支持切换时所提供的动态方法。2.动态方法的添加存在覆盖关系。

    示例9:

    A.metaClass.character?=?'Cat?in?the?Hat'??

  • def?a1?=?assert?a1.character?==?'Cat?in?the?Hat'??
  • def?ourProperties?=?Collections.synchronizedMap([:])??
  • A.metaClass.setType?=?{String?value?->?ourProperties["${delegate}Type"]?=?value?}??
  • A.metaClass.getType?=?{?->?ourProperties["${delegate}Type"]}??
  • a1.type?=?'Hatted?Cat'??
  • assert?a1.type?==?'Hatted?Cat'??
  • A.metaClass.constructor?=?{?->?new?A()}??
  • ????a2?=?catch(Error?e){??
  • assert?e?in?StackOverflowError??
  • A.metaClass.constructor?=?{String?s?->?new?A(character?:s)}??
  • a2?=?new?A("Thing?One")??
  • A.metaClass.'changeCharacterToThingTwo'=?{->?delegate.character?=?'Thing?Two'?}??
  • a2.character=?'Cat?in?the?Hat'??
  • a2.changeCharacterToThingTwo()??
  • assert?a2.character?==?'Thing?Two'??
  • ['Hatted?Cat',?'Thing',?'Boy',?'Girl',?'Mother'].each{p->??
  • ??A.metaClass."changeTypeTo${p}"=?{->?delegate.type=?p}??
  • a2.changeTypeToBoy()??
  • assert?a2.type?==?'Boy'??
  • a2.'changeTypeToHatted?Cat'()??
  • assert?a2.type?==?'Hatted?Cat'??
  • 该示例的内容只要是示例8的一个补充,我们不仅可以动态添加方法,同时还可以动态添加属性和构造方法。

    示例10:

    ExpandoMetaClass.enableGlobally()??

  • //call?'enableGlobally'?method?before?adding?to?supplied?class??
  • List.metaClass.sizeDoubled?=?{->?delegate.size()?*?2?}??
  • //add?method?to?an?interface??
  • def?list?=?[]?<<?1?<<?2??
  • assert?list.sizeDoubled()?==?4??
  • ?该示例比较简介,旨在告诉我们我们不仅可以对自定义的groovy对象进行属性方法等的动态添加,同样的我们可以对非自定义的Groovy提供的对象进行动态处理。处理方法和自定义对象的方法时完全一样的,唯一的区别在于,我们需要额外定义ExpandoMetaClass.enableGlobally(),然而笔者发现笔者使用的1.8.1版的groovy如果去掉了该声明也是可以工作

    所以请各位读者按照个人版本做尝试。

    示例11:

    class?Bird{??
  • ????def?name?=?'Tweety'??
  • ????def?twirp(){?'i?taught?i?saw?a?puddy?cat'?}??
  • Bird.metaClass.invokeMethod?=?{name,?args->??
  • ????def?metaMethod?=?Bird.metaClass.getMetaMethod(name,?args)??
  • ????metaMethod?metaMethod.invoke(delegate,args):?'no?such?method'??
  • new?Bird()??
  • assert?a.twirp()?==?'i?taught?i?saw?a?puddy?cat'??
  • assert?a.bleet()?=='no?such?method'??
  • Bird.metaClass.getProperty?=?{name->??
  • ????def?metaProperty?=?Bird.metaClass.getMetaProperty(name)??
  • ????metaProperty?metaProperty.getProperty(delegate):?'no?such?property'???
  • def?b?=?assert?b.name?==?'Tweety'??
  • assert?b.filling?==?'no?such?property'??
  • ?该示例主要说明的是我们不仅可以用Expando特性来添加方法属性和构造方法,同样的我们可以对已经存在的方法进行覆盖。最后还要强调一下Bird.metaClass返回的是ExpandoMetaClass我们这里覆盖的getMetaMethod和

    getProperty以及invokeMethod和getProperty方法都是对ExpandoMetaClass类和它父类对应的方法进行覆盖。

    至此,Groovy的MetaClass内容已经介绍完了。该部分内容在groovy中非常重要。谨此记录下来作为日后参考,同时希望对大家有帮助。

    (编辑:李大同)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

      推荐文章
        热点阅读