前言
之前一直使用greys及其内部升级二次开发版来排查问题。最近周末刚好事情不多,作为一名程序员本能地想要弄懂这么神奇的greys到底是怎么实现的?周末从github上拉了代码仔细读了读,其基本技术框架是JVM attach + Instrumentation + asm实现的。关于JVM attach和Instrumentation的功能,下次再写文章介绍,本文着重于greys中非常神奇的一个类AdviceWeaver,该类使用asm代码实现了简单的aop功能,本文的实现方式基本参考该类,具体的代码放在了scrat-profiler模块中。下文将结合asm的使用方法讲解如何实现简单的aop功能。
asm简介
什么是asm?ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为(摘自网友翻译)。asm的文档请参考asm文档,文档写的比较全。主要几个重要的类为ClassReader、ClassWriter、ClassVisitor、MethodVisitor等。
ClassVisitor、MethodVisitor与AdviceAdapter
在使用ASM操作字节码之前,我们需要稍微了解下ClassVisitor,ClassVisitor用来generating and transforming compiled classes,ClassVisitor中的每个方法都对应了class file的相关部分,ClassVisitor里方法的执行顺序应该如下:
visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )*
( visitInnerClass | visitField | visitMethod )*
visitEnd
由于本文主要讲的是enhance class,所以着重讨论下ClassVisitor Transforming classes。Transforming classes通常需要借助于两个类ClassReader与ClassWriter,ClassWriter是ClassVisitor子类,与ClassReader或者其他ClassVisitor子类一起使用来generate a modified class from one or more existing Java classes。典型的用法如下:
byte[] b1 = ...;
ClassWriter cw = new ClassWriter(0);
ClassReader cr = new ClassReader(b1);
cr.accept(cw, 0);
byte[] b2 = cw.toByteArray();
与其他ClassVisitor一起使用的典型用法如下:
byte[] b1 = ...;
ClassWriter cw = new ClassWriter(0);
// cv forwards all events to cw
ClassVisitor cv = new ClassVisitor(ASM4, cw) { };
ClassReader cr = new ClassReader(b1);
cr.accept(cv, 0);
byte[] b2 = cw.toByteArray();
由于我想实现的是方法级别的AOP增强,所以我更加关注与ClassVisitor的visitMethod方法:
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
if (cv != null) {
return cv.visitMethod(access, name, desc, signature, exceptions);
}
return null;
}
可以看到的是该方法返回的是MethodVisitor,当我们继承ClassVisitor并且复写visitMethod返回自定义的MethodVisitor时,我们可以实现对method的字节码进行替换增强。首先我们来研究下MethodVisitor如何使用。
MethodVisitor接口中的方法调用必须按照以下的顺序:
visitAnnotationDefault?
( visitAnnotation | visitParameterAnnotation | visitAttribute )*
( visitCode
( visitTryCatchBlock | visitLabel | visitFrame | visitXxxInsn |
visitLocalVariable | visitLineNumber )*
visitMaxs )?
visitEnd
摘取ASM user guide中比较重要的几句话: