概述
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的探讨所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复