ASM是Java字节码操作与分析框架,我们可以动态生成类或者增强既有类的功能。它可以直接生成二进制.class文件,也可以在类被加入Java虚拟机之前动态的改变现有类的行为。ASM框架中几个核心类如下:
- ClassReader:解析编译过的.class字节码文件
- ClassWriter:重新构建编译后的类,比如修改类名,属性及方法,甚至可以生成新的类字节码文件
- ClassVisitor:主要负责“访问”类成员信息,包括在类上的注解,构造方法,类的字段,类的方法,静态代码块等
- AdviceAdapter:实现了MethodVisitor接口,主要负责“访问”方法的信息,用来进行具体的方法字节码操作 ClassVisitor的主要方法介绍如下:
class MyClassVisitor extends ClassVisitor {
MyClassVisitor(ClassVisitor classVisitor) {
//参数为ASM版本号,classVisitor
super(Opcodes.ASM5, classVisitor)
}
/**
* 可以拿到类的全部信息,然后对满足条件的类进行过滤,遍历类开始的时候调用该方法
* @param version:JDK的版本
* @param access:类的修饰符,它是以ACC_开头的常量,有ACC_PUBLIC(public),ACC_PRIVATE(private),ACC_PROTECTED(protected),
* ACC_FINAL(final),ACC_SUPER(extends),ACC_INTERFACE(接口),ACC_ABSTRACT(抽象类),ACC_ANNOTATION(注解类型)
* ACC_ENUM(枚举类型),ACC_DEPRECATED(标记了@Deprecated的类),ACC_SYNTHETIC(Java生成)
* @param name:类的名称。它是以路径的形式显示的(a.b.c.MyClass的显示为a/b/c/MyClass,并且使不带".class"后缀的)
* @param signature:泛型信息,如果未定义任何泛型,则参数为空
* @param superName:当前类所继承的父类
* @param interfaces:该类所实现的接口列表。因为一个类可以实现多个不同的接口,所以是个数组
*/
@Override
void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces)
}
/**
* 访问内部类
* @param name
* @param outerName
* @param innerName
* @param access
*/
@Override
void visitInnerClass(String name, String outerName, String innerName, int access) {
super.visitInnerClass(name, outerName, innerName, access)
}
/**
* 拿到要修改的方法,然后进行修改操作,扫描器扫描到类的方法时会调用
* @param access:表示方法的修饰符。也是以ACC_开头的常量。除了和类共用的那些public,private等,方法中独有的修饰符为ACC_STATIC(static)
* ACC_SYNCHRONIZED(同步的),ACC_VARARGS(不定参数个数的方法),ACC_NATIVE(native类型的方法)
* @param name:方法名
* @param desc:方法签名。格式为:(参数列表)返回值类型。比如方法为void setText(String s),它的desc属性值为:(Ljava/lang/String;)V
* 参数类型(数组为[..;)(long型为J)(boolean型为Z)(对象型为L)
* @param signature:表示与泛型相关的信息
* @param exceptions:表示方法会抛出的异常,如果不会抛出异常则参数为空
* @return
*/
@Override
MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return super.visitMethod(access, name, desc, signature, exceptions)
}
/**
* 遍历类中成员信息结束
*/
@Override
void visitEnd() {
super.visitEnd()
}
}
关于methodVisitor的说明:每次调用该方法时需要返回一个新的MethodVisitor实例,不应该返回一个以前用过的Visitor也就是说在这个方法中我们要重新new一个,比如下面
@Override
MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor methodVisitor = super.visitMethod(access, name, desc, signature, exceptions)
String nameDesc = name + desc
methodVisitor = new SensorsAnalyticsDefaultMethodVisitor(methodVisitor, access, name, desc) {
boolean isSensorsDataTrackViewOnClickAnnotation = false
@Override
protected void onMethodExit(int opcode) {
super.onMethodExit(opcode)
if (isSensorsDataTrackViewOnClickAnnotation) {
methodVisitor.visitLdcInsn(visitor.typeName)
methodVisitor.visitMethodInsn(INVOKESTATIC, SDK_API_CLASS, "trackViewOnClick", "(Ljava/lang/String;)V", false)
}
}
@Override
AnnotationVisitor visitAnnotation(String s, boolean b) {
if (s == 'Lplugin/custom/mwy/com/sdk/SensorsDataTrackViewOnClick;') {
isSensorsDataTrackViewOnClickAnnotation = true
}
visitor = new MyAnnotationVisitor(super.visitAnnotation(s, b))
return visitor
}
}
return methodVisitor
}
methodVisitor常用的方法有:onMethodEnter(方法开始的时候调用)、onMethodExit(方法结束时调用)、visitAnnotation(方法有注解时调用)。比如需要统计方法的耗时情况我们直接在onMethodEnter和onMethodExit中记录时间即可。如果要想埋点的话,一般在onMethodExit中进行打点,同时也可以配合注解来进行(比如上面就定义了一个注解)