我是靠谱客的博主 呆萌发夹,最近开发中收集的这篇文章主要介绍7、Groovy核心Metaclass的探讨,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

2021SC@SDUSC

Groovy核心Metaclass的探讨

问题的引入:

在上篇博客里面,我们看到了在拓展方法和对象的反射关联上面,存在一个叫做Metaclass的类,它的作用是为类动态载入了invoke方法。下面就让我对Metaclass进行一下源码的探讨。

Metaclass的作用功能:

Metaclass是Groovy里面的一个核心特点类,简单来说就是可以通过Metaclass来定义Class的Class,Class定义了该类实例的行为,Metaclass则定义了该类及其实例的行为。换句话来说,Groovy通过Metaclass使程序可以在运行时修改/添加类的方法、属性等,这时Metaclass的核心及其基础用法。例:

// 给String类添加了一个名为capitalize的方法
String.metaClass.capitalize = { -> delegate[0].toUpperCase() + delegate[1..-1] }
// 给String类添加了一个名为spaceCount的只读属性
String.metaClass.getSpaceCount = { -> delegate.count(' ') }

Metaclass的注册

简单了解了一下Metaclass后,我们来探讨一下Metaclass的注册流程和存放位置。
在查找Metaclass相关的类的时候,我发现有个类是groovy.lang.MetaClassRegistry,我点进去发现这个类只是一个interfere,里面有一个虚拟方法getMetaClass,便在全部文件里面查找了下实现类,发现只有一个实现类是org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl。

public final MetaClass getMetaClass(Class theClass) {
    return ClassInfo.getClassInfo(theClass).getMetaClass();
}

而在其中的ClassInfo的getClassInfo静态方法:

public static ClassInfo getClassInfo(Class cls) {
    // localMapRef的类型是WeakReference<ThreadLocalMapHandler>
    ThreadLocalMapHandler handler = localMapRef.get();
    SoftReference<LocalMap> ref=null;
    if (handler!=null) ref = handler.get();
    LocalMap map=null;
    if (ref!=null) map = ref.get();
    if (map!=null) return map.get(cls);
    // 只有当localMapRef或ref已被回收时,才会调用下面的代码
    // globalClassSet的类型是ClassInfoSet
    return (ClassInfo) globalClassSet.getOrPut(cls,null);
}

在getClassInfo里面,我们可以知道,所有的Metaclass实例都是存储在一个通过localMapRef.get().get()得到的一个map里面的,这个全局的Map不需要对key(Class)进行排序,所以可以使用HashMap;通过是否是同一个实例来判断key的相等性,所以是一个IdentityHashMap。
最后做一个总结就是,MetaClass的注册和查找在getMetaClass(Class)方法中,查找到Class对应的ClassInfo后,再调用ClassInfo的getMetaClass方法获得Class对应的Metaclass。

Groovy类中Metaclass的指针和更改

我们随意编写一个Groovy的类对象,通过反编译观看源码,查看类如何与它的Metaclass是如何联系的。
我们在之前第二篇博客Groovy编译原理的学习可以了解到,我们可以通过groovyc命令进行反编译把一个Groovy编写的.groovy的类编译成class类,这个方便我们查看。
先编写个最简单了Groovy类POGO.groovy:

class POGO {} // 这是一个用Groovy编写的对象

def pogo = new POGO()
assert pogo.metaClass == POGO.metaClass // 默认情况下POGO实例的Metaclass与类的Metaclass相同

pogo.metaClass.hello = { -> println 'Hello' }
assert pogo.metaClass != POGO.metaClass // 修改POGO实例的Metaclass后,该POGO实例将拥有独立的Metaclass

然后进行反编译后查看可以得到一下代码:

public class POGO implements GroovyObject { // 用Groovy编写的类都实现了GroovyObject接口
    ...
    private transient MetaClass metaClass; // 用于存储实例对应的Metaclass
    ...
    public MetaClass getMetaClass() { // 实现了GroovyObject.getMetaClass()方法
        if (metaClass != null)
            return metaClass;
        metaClass = $getStaticMetaClass(); // 默认情况下返回类的Metaclass
        return metaClass;
    }

    public void setMetaClass(MetaClass metaClass) { // 实现了GroovyObject.setMetaClass(MetaClass)方法
        this.metaClass = metaClass;
    }

    protected MetaClass $getStaticMetaClass() {
        ClassInfo classinfo = $staticClassInfo;
        if (classinfo == null)
            $staticClassInfo = classinfo = ClassInfo.getClassInfo(getClass());
        return classinfo.getMetaClass();
    }
    ...
}

这样就很容易看出,POGO都实现了GroovyObject接口,对象实现了GroovyInterceptable接口,那么所有的调用都会被路由给它的invokeMethod(),而该接口中的getMetaClass和setMetaClass方法分别负责实例对应的Metaclass的读和写。而POGO实例对应的Metaclass则直接存放在实例中的metaClass字段中。

groovy.lang.GroovyObject是Groovy中的主要接口,就像Object类是Java中的主要接口一样。GroovyObject在groovy.lang.GroovyObjectSupport类中有一个默认实现,它负责将调用传递给groovy.lang.MetaClass对象。 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);
    }

invokeMethod:
当调用的方法不存在于Groovy对象上时,将调用此方法

    class Person {
        def run() {
            println "run"
        }
        /**
         * 当您调用的方法不存在于Groovy对象上时,将调用此方法
         */
        def invokeMethod(String name, Object args) {
            println "called invokeMethod $name $args"
        }
    }
    def person = new Person()
    //输出:run
    person.run()
    //输出:called invokeMethod eat []
    person.eat()

最后

以上就是呆萌发夹为你收集整理的7、Groovy核心Metaclass的探讨的全部内容,希望文章能够帮你解决7、Groovy核心Metaclass的探讨所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(49)

评论列表共有 0 条评论

立即
投稿
返回
顶部