上一篇文章我们讲解了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中类的扩展。下面,我再将以上的整个流程用一个流程图来展示,让大家看起来更加的直观:
热门评论
好像还是没回答:一个实例对象 和 文章中的代理是如何绑定的?