2021SC@SDUSC
上次我们分析了groovy的几个核心扩展类,主要是几个XXXGroovyMethods,通过这几个类的扩展方法,可以让我们编写的POJO和POGO类有更多常用的方法,可以极大的提高我们的开发效率。从整体上知道groovy SDK对JDK做了哪些扩展。
了解了这些扩展以后,我产生了一个疑问,这些都是GDK提供的新类,这些方法并没有加到JDK中对应的类中,我们是如何可以直接调用它扩展的那些方法的呢,例如,我们DefaultGroovyMethods类中为我们任意对象都提供了一个each方法,使用如下:
[1,2,3,4,5].each{
print it
}
这里[1,2,3,4,5]其实就是创建了一个Java的ArrayList,而JDK中的ArrayList并没有each这个方法,而each方法又是DefaultGroovyMethods这个类中的一个方法,为什么我们创建的类的对象就可以像调用自己内部的方法一样,调用DefaultGroovyMethods提供的方法呢,这就是我们这次重点要分析的内容。
下面,我们就通过源码来分析一下这个过程。
第一步: 在编译Groovy自身的代码时,groovy编译器会调用GDK中的DgmConvertero类的main方法来将DefaultGroovyMethods中所有的方法都生成一个包装类,生成的这个包装类继承于GeneratedmetaMethod类,而GeneratedmetaMethod类则继承metaMethod类。该包装类的类名类似于org.codehaus.groovy.runtime.dgm$123($后跟一个数),在Groovy分发的jar包中可以找到总共1317个这样的类,说明DGM中总共有1317个方法。下面我们就先来看一下DgmConvertero类的main方法是如何生成方法对应的包装类的,核心代码如下:
package org.codehaus.groovy.tools;
//生成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 cachedMethodsList = new ArrayList();
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(CachedMethod.EMPTY_ARRAY);
List records = new ArrayList();
cur = 0;
CachedMethod[] var25 = cachedMethods;
int var8 = cachedMethods.length;
//开始遍历整个cachedMethodsList中右存的方法
for(int var9 = 0; var9 < var8; ++var9) {
CachedMethod method = var25[var9];
//只对public static类型的方法生成对应的方法包装类
if (method.isStatic() && method.isPublic() && method.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字节码
File targetFile = (new File(targetDirectory + className + ".class")).getCanonicalFile();
targetFile.getParentFile().mkdirs();
FileOutputStream fileOutputStream = new FileOutputStream(targetFile);
try {
fileOutputStream.write(bytes);
fileOutputStream.flush();
} catch (Throwable var22) {
try {
fileOutputStream.close();
} catch (Throwable var21) {
var22.addSuppressed(var21);
}
throw var22;
}
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类,如图:
3.0.9这个版本总共有1317多个这样的dgmXXX类。我们随便来看其中的一个类的代码:
public class dgm$17 extends GeneratedmetaMethod {
public dgm$17(String var1, CachedClass var2, Class var3, Class[] var4) {
super(var1, var2, var3, var4);
}
public Object invoke(Object var1, Object[] var2) {
return DefaultTypeTransformation.box(DefaultGroovyMethods.any((Map)var1, (Closure)var2[0]));
}
public final Object doMethodInvoke(Object var1, Object[] var2) {
var2 = this.coerceArgumentsToClasses(var2);
return DefaultTypeTransformation.box(DefaultGroovyMethods.any((Map)var1, (Closure)var2[0]));
}
}
从代码中,我们可以看到,这个dgm$17类,就是我们DefaultGroovyMethods类中的box()这个方法的包装类,其他的类则对应着其他的方法。
第二步,有了上面的各种包装类以后,我们接下来要看的一个类就是: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> 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> map) { //由于我们上面第一处调用时传的true,所以我们只看if部分 if(useMethodWrapper) { try { //从前面我们保存的dmginfo文件中取出我们在编译时生成的所有DgmMethodRecord类,所以如果useMethodWrapper为true,就是代表处理生成的所有包装类 List 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 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 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 {
private volatile metaMethod proxy;
private final String className;
public Object invoke(Object object, Object[] arguments) {
//最后就调用了对应包装类中的invoke方法,从而完成了我们整个的一个调用链
return proxy().invoke(object, arguments);
}
public final synchronized metaMethod proxy() {
// 第一次调用时,通过createProxy创建包装类实例
if (proxy == null) {
createProxy();
}
return proxy;
}
private void createProxy() {
try {
// 载入包装类并进行实例化
Class> aClass = getClass().getClassLoader().loadClass(className.replace('/','.'));
Constructor> constructor = aClass.getConstructor(String.class, CachedClass.class, Class.class, Class[].class);
proxy = (metaMethod) constructor.newInstance(getName(), getDeclaringClass(), getReturnType(), getNativeParameterTypes());
}
catch (Throwable t) {
t.printStackTrace();
throw new GroovyRuntimeException("Failed to create DGM method proxy : " + t, t);
}
}
}
到此,groovy中的XXXGroovyMethods类就通过我们以上分析的这个流程绑定到了Java对象中去,从而完成了对java中类的扩展。下面,我再将以上的整个流程用一个流程图来展示,让其更加的直观:



