手记

Groovy核心类源码讲解(下)

2018-07-16 14:48:096784浏览

qndroid

1实战 · 15手记 · 17推荐

上一篇文章我们讲解了groovy相关的几个核心扩展类,主要是几个XXXGroovyMethods,通过这几个类的扩展方法,可以让我们编写的POJO和POGO类有更多常用的方法,可以极大的提高我们的开发效率,上一篇我们重点以text这个方法去举的例子。好,通过上篇文章的学习,大家可以从整体上知道groovy SDK对JDK做了那些扩展(还有一部分的扩展,我们后面讲)。

了解了有那些扩展以后,想必大家可能会有一些疑问了,这些都是GDK提供的新类,这此方法并没有加到JDK中对应的类中,我们是如何可以直接调用它扩展的那些方法的呢,例如,我们DefaultGroovyMethods类中为我们任意对象都提供了一个each方法,使用如下:

[1,2,3,4,5].each{
   print it
}

学过groovy的同学都知道[1,2,3,4,5]这个其实就是创建了一个Java的ArrayList,而JDK中的ArrayList并没有each这个方法,而each方法又是DefaultGroovyMethods这个类中的一个方法,为什么我们创建的类的对象就可以像调用自己内部的方法一样,调用DefaultGroovyMethods提供的方法呢,这就是我们本篇文章重点要讲解的内容,通过本文章的学习,大家就可以明白,为什么我们我们创建的各种对象可以直接调用XXXGroovyMethods类中的各种方法。

下面,我们就通过源码来分析一下这个过程。

第一步: 在编译Groovy自身的代码时,groovy编译器会调用GDK中的DgmConvertero类的main方法来将DefaultGroovyMethods中所有的方法都生成一个包装类,生成的这个包装类继成于GeneratedMetaMethod类,而GeneratedMetaMethod类则继承MetaMethod类。该包装类的类名类似于org.codehaus.groovy.runtime.dgm$123($后跟一个数),在Groovy分发的jar包中可以找到总共1171个这样的类,说明DGM中总共有1171个方法。下面我们就先来看一下DgmConvertero类的main方法是如何生成方法对应的包装类的,核心代码如下:

//生成DGM方法包装类的核心类
public class DgmConverter implements Opcodes {
  public DgmConverter() {
  }

 //生成DGM方法包装类的核心方法
  public static void main(String[] args) throws IOException {
    String targetDirectory = "target/classes/";
    boolean info = args.length == 1 && args[0].equals("--info") || args.length == 2 && args[0].equals("--info");
    if(info && args.length == 2) {
      targetDirectory = args[1];
      if(!targetDirectory.endsWith("/")) {
        targetDirectory = targetDirectory + "/";
      }
    }

    List<CachedMethod> cachedMethodsList = new ArrayList();
    /**
     * 
    Class[] DGM_LIKE_CLASSES = new Class[]{DefaultGroovyMethods.class, DateGroovyMethods.class,EncodingGroovyMethods.class, IOGroovyMethods.class, ProcessGroovyMethods.class,
   ResourceGroovyMethods.class, SocketGroovyMethods.class, StringGroovyMethods.class}; 
这里我把DefaultGroovyMethods.DGM_LIKE_CLASSES的代码注释进来,大家可以清楚的看到var4这个List就是我们所有的XXXGroovyMethods的类字节码
    **/
    Class[] var4 = DefaultGroovyMethods.DGM_LIKE_CLASSES;
    int var5 = var4.length;

   //将所有XXXGroovyMethods类中的方法都统一添加到一个Collections中去,放到一个集合中(就是我们刚刚上面定义的cachedMethodsList),方便我们后面的处理
    int cur;
    for(cur = 0; cur < var5; ++cur) {
      Class aClass = var4[cur];
      Collections.addAll(cachedMethodsList, ReflectionCache.getCachedClass(aClass).getMethods());
    }

    CachedMethod[] cachedMethods = (CachedMethod[])cachedMethodsList.toArray(new CachedMethod[cachedMethodsList.size()]);
    List<DgmMethodRecord> records = new ArrayList();
    cur = 0;
    CachedMethod[] var20 = cachedMethods;
    int var8 = cachedMethods.length;

    //开始遍历整个cachedMethodsList中右存的方法
    for(int var9 = 0; var9 < var8; ++var9) {
      CachedMethod method = var20[var9];
    //只对public static类型的方法生成对应的方法包装类
    if(method.isStatic() && method.isPublic() && method.getCachedMethod().getAnnotation(Deprecated.class) == null && method.getParameterTypes().length != 0) {

         Class returnType = method.getReturnType();
        //这里就是生成类的类名,一会我们会通过截图看到有许多这样的类,大家就可以知道这些类就是在这个时候动态生成的 
        String className = "org/codehaus/groovy/runtime/dgm$" + cur++;
        // 通过DgmMethodRecord类来记录一个方法中所有的信息
       DgmMethodRecord record = new DgmMethodRecord();
        records.add(record);
        record.methodName = method.getName();
        record.returnType = method.getReturnType();
        record.parameters = method.getNativeParameterTypes();
        record.className = className;
       //通过Groovy中的ClassWriter类来创建对应的包装类字节码,注意,不是生成源文件而是字节码,因为此时已经在编译器了,字节码才是真正可用的
       ClassWriter cw = new ClassWriter(1);
        cw.visit(47, 1, className, (String)null, "org/codehaus/groovy/reflection/GeneratedMetaMethod", (String[])null);
       //为类字节码创建各个方法 
        createConstructor(cw);
        String methodDescriptor = BytecodeHelper.getMethodDescriptor(returnType, method.getNativeParameterTypes());
        createInvokeMethod(method, cw, returnType, methodDescriptor);
        createDoMethodInvokeMethod(method, cw, className, returnType, methodDescriptor);
        createIsValidMethodMethod(method, cw, className);
        cw.visitEnd();
        byte[] bytes = cw.toByteArray();
        //将生成的byte[]字节码写入到最终的class文件中去,从而生成一个可用的class字节码 
       FileOutputStream fileOutputStream = new FileOutputStream(targetDirectory + className + ".class");
        fileOutputStream.write(bytes);
        fileOutputStream.flush();
        fileOutputStream.close();
      }
    }
    //将生成的包装类与DGM中对应的方法形成一个映射关系,存到dgminfo文件中去,这样才能正确的知道那个方法对应着那个包装类类
    DgmMethodRecord.saveDgmInfo(records, targetDirectory + "/META-INF/dgminfo");
    if(info) {
      System.out.println("Saved " + cur + " dgm records to: " + targetDirectory + "/META-INF/dgminfo");
    }
  }
}

好,这个方法执行完毕以后,我们所有的DGM(XXXGroovyMethods)类中的方法都对应的生成了一个dgmXXX类,如图:

当然,我这里并没有全部截图出来,2.4.11这个版本总共有1100多个这样的dgmXXX类,到此大家应该知道,这些类的作用,已经是如何生成的。我们随便来看其中的一个类的代码:

public class dgm$64 extends GeneratedMetaMethod {
  public dgm$64(String var1, CachedClass var2, Class var3, Class[] var4) {
    super(var1, var2, var3, var4);
  }

  public Object invoke(Object var1, Object[] var2) {
    return DefaultGroovyMethods.collect((Collection)var1);
  }

  public final Object doMethodInvoke(Object var1, Object[] var2) {
    this.coerceArgumentsToClasses(var2);
    return DefaultGroovyMethods.collect((Collection)var1);
  }
}

从代码中,我们可以看到,这个dmg$64类,就是我们DefaultGroovyMethods类中的collect()这个方法的包装类,大家也可以看一看其它的包装类,一定都是这样的,好了,第一步生成了对应的包装类以后呢,就结束了,下面我们继续往下分析。

第二步,有了上面的各种包装类以后,我们要生点看的一个类就是:MetaClassRegistryImpl,这个类是由groovy程序在运行前调用,他会为我们编译时生成的所有包装类再统一生成GeneratedMetaMethod.Proxy代理类,核心代码如下:

public class MetaClassRegistryImpl implements MetaClassRegistry {
  //最终执行的初始化函数
 public MetaClassRegistryImpl(int loadDefault, boolean useAccessible) {
    this.instanceMethods = new FastArray();
    this.staticMethods = new FastArray();
    this.changeListenerList = new LinkedList();
    this.nonRemoveableChangeListenerList = new LinkedList();
    this.metaClassInfo = new ManagedConcurrentLinkedQueue(ReferenceBundle.getWeakBundle());
    this.moduleRegistry = new ExtensionModuleRegistry();
    this.metaClassCreationHandle = new MetaClassCreationHandle();
    this.useAccessible = useAccessible;
    if(loadDefault == 0) {
      Map<CachedClass, List<MetaMethod>> map = new HashMap();
      //调用registerMethods来为所有的DefaultGroovyMethods中的包装类来创建Proxy
      this.registerMethods((Class)null, true, true, map);
      //以下的都是为指定的Class字节码中的方法创建Proxy,可以不用细看
      Class[] additionals = DefaultGroovyMethods.additionals;

      for(int i = 0; i != additionals.length; ++i) {
        this.createMetaMethodFromClass(map, additionals[i]);
      }

      Class[] pluginDGMs = VMPluginFactory.getPlugin().getPluginDefaultGroovyMethods();
      Class[] staticPluginDGMs = pluginDGMs;
      int var7 = pluginDGMs.length;

      int var8;
      for(var8 = 0; var8 < var7; ++var8) {
        Class plugin = staticPluginDGMs[var8];
        this.registerMethods(plugin, false, true, map);
      }

      this.registerMethods(DefaultGroovyStaticMethods.class, false, false, map);
      staticPluginDGMs = VMPluginFactory.getPlugin().getPluginStaticGroovyMethods();
      Class[] var13 = staticPluginDGMs;
      var8 = staticPluginDGMs.length;

      for(int var15 = 0; var15 < var8; ++var15) {
        Class plugin = var13[var15];
        this.registerMethods(plugin, false, false, map);
      }

      ExtensionModuleScanner scanner = new ExtensionModuleScanner(new MetaClassRegistryImpl.DefaultModuleListener(map), this.getClass().getClassLoader());
      scanner.scanClasspathModules();
      refreshMopMethods(map);
    }

    this.installMetaClassCreationHandle();
    MetaClass emcMetaClass = this.metaClassCreationHandle.create(ExpandoMetaClass.class, this);
    emcMetaClass.initialize();
    ClassInfo.getClassInfo(ExpandoMetaClass.class).setStrongMetaClass(emcMetaClass);
    this.addNonRemovableMetaClassRegistryChangeEventListener(new MetaClassRegistryChangeEventListener() {
      public void updateConstantMetaClass(MetaClassRegistryChangeEvent cmcu) {
        synchronized(MetaClassRegistryImpl.this.metaClassInfo) {
          MetaClassRegistryImpl.this.metaClassInfo.add(cmcu.getNewMetaClass());
          DefaultMetaClassInfo.getNewConstantMetaClassVersioning();
          Class c = cmcu.getClassToUpdate();
          DefaultMetaClassInfo.setPrimitiveMeta(c, cmcu.getNewMetaClass() == null);

          try {
            Field sdyn = c.getDeclaredField("__$stMC");
            sdyn.setBoolean((Object)null, cmcu.getNewMetaClass() != null);
          } catch (Throwable var7) {
            ;
          }

        }
      }
    });
  }
}

通过上面代码,我们可以知道,registerMethods()这个方法是最核心的,下面,我们就来看一下这个方法中的代码:

private void registerMethods(Class theClass, boolean useMethodWrapper, boolean useInstanceMethods, Map<CachedClass, List<MetaMethod>> map) {
  //由于我们上面第一处调用时传的true,所以我们只看if部分
 if(useMethodWrapper) {
      try {
        //从前面我们保存的dmginfo文件中取出我们在编译时生成的所有DgmMethodRecord类,所以如果useMethodWrapper为true,就是代表处理生成的所有包装类
        List<DgmMethodRecord> records = DgmMethodRecord.loadDgmInfo();
        Iterator var6 = records.iterator();
       //开始遍历所有DgmMethodRecord
        while(var6.hasNext()) {
          DgmMethodRecord record = (DgmMethodRecord)var6.next();
          Class[] newParams = new Class[record.parameters.length - 1];
          System.arraycopy(record.parameters, 1, newParams, 0, record.parameters.length - 1);
         //为每个DgmMethodRecord中保存的包装类生成一个Proxy对象。 
         MetaMethod method = new Proxy(record.className, record.methodName, ReflectionCache.getCachedClass(record.parameters[0]), record.returnType, newParams);
         //获取调用此方法所在的类
        CachedClass declClass = method.getDeclaringClass();

        List<MetaMethod> arr = (List)map.get(declClass);
          if(arr == null) {
            arr = new ArrayList(4);
            map.put(declClass, arr);
          }
          ((List)arr).add(method);
          //最重要,将生成的Proxy对象添加到instanceMethods(FastArray)中,
          this.instanceMethods.add(method);
        }
      } catch (Throwable var14) {
        var14.printStackTrace();
      }
    } else {
      //处理指定参数的类,不再我们分析范围中,基本分析流程一样,大家也基本可以看懂
      CachedMethod[] methods = ReflectionCache.getCachedClass(theClass).getMethods();
      CachedMethod[] var16 = methods;
      int var17 = methods.length;

      for(int var18 = 0; var18 < var17; ++var18) {
        CachedMethod method = var16[var18];
        int mod = method.getModifiers();
        if(Modifier.isStatic(mod) && Modifier.isPublic(mod) && method.getCachedMethod().getAnnotation(Deprecated.class) == null) {
          CachedClass[] paramTypes = method.getParameterTypes();
          if(paramTypes.length > 0) {
            List<MetaMethod> arr = (List)map.get(paramTypes[0]);
            if(arr == null) {
              arr = new ArrayList(4);
              map.put(paramTypes[0], arr);
            }

            if(useInstanceMethods) {
              NewInstanceMetaMethod metaMethod = new NewInstanceMetaMethod(method);
              ((List)arr).add(metaMethod);
              this.instanceMethods.add(metaMethod);
            } else {
              NewStaticMetaMethod metaMethod = new NewStaticMetaMethod(method);
              ((List)arr).add(metaMethod);
              this.staticMethods.add(metaMethod);
            }
          }
        }
      }
    }
  }

到这里,我们看可以看,我们groovy应用启动以后,就会首先由RootClassLoad去加载MetaClassRegistryImpl并调用其初始化函数,当他的初始化函数执行完毕以后,我们的每个DGM中的包装类,又有一个一一对应的Proxy类。

第三步, Groovy本身的类在初始化完成以后,开始真正的执行我们自己的groovy代码,无论是脚本还是应用程序,这个时候当调用到对应的DGM方法时,首先看这个类中是否已经有此方法,有,则调用类中已有的,如果没有,则去MetaClassRegistryImpl中再去继续找此方法,如果找不到,找到调用,找不到,报methodmissingException。下面我们来看一下Proxy中的关键代码:

public static class Proxy extends GeneratedMetaMethod {
 2     private volatile MetaMethod proxy;
 3     private final String className;
 4     
 5     public Object invoke(Object object, Object[] arguments) {
 6        //最后就调用了对应包装类中的invoke方法,从而完成了我们整个的一个调用链
          return proxy().invoke(object, arguments);
 7     }
 8 
 9     public final synchronized MetaMethod proxy() {
10         // 第一次调用时,通过createProxy创建包装类实例
11         if (proxy == null) {
12             createProxy();
13         }
14         return proxy;
15     }
16 
17     private void createProxy() {
18         try {
19             // 载入包装类并进行实例化
20             Class<?> aClass = getClass().getClassLoader().loadClass(className.replace('/','.'));
21             Constructor<?> constructor = aClass.getConstructor(String.class, CachedClass.class, Class.class, Class[].class);
22             proxy = (MetaMethod) constructor.newInstance(getName(), getDeclaringClass(), getReturnType(), getNativeParameterTypes());
23         }
24         catch (Throwable t) {
25             t.printStackTrace();
26             throw new GroovyRuntimeException("Failed to create DGM method proxy : " + t, t);
27         }
28     }
29 }

到此,groovy中的XXXGroovyMethods类就通过我们以上分析的这个流程绑定到了Java对象中去,从而完成了对java中类的扩展。下面,我再将以上的整个流程用一个流程图来展示,让大家看起来更加的直观:

这里重点是对源码的一些分析,如何对groovy基础和用法有不了的同学,可以通过我的实战课程来系统的学习一下。

7人推荐
随时随地看视频
慕课网APP

热门评论

好像还是没回答:一个实例对象 和 文章中的代理是如何绑定的?

查看全部评论