Class Transformation
ClassReader
ClassWriter用于生成字节码文件,而ClassReader用于读取字节码文件
配合字节码文件的结构去理解
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}classFileBuffer:读取到的字节码数据cpInfoOffsets:字节码中的常量池(constant pool)的位置header:字节码的访问标识符的位置
构造器读取字节码后,会根据class文件的结构填充这几个字段。
用ClassReader读取如下类的基本信息:
access: 33
className: sample/HelloWorld
superName: java/lang/Exception
interfaces: [java/io/Serializable, java/lang/Cloneable]
ClassReader提供一个accept方法来让ClassVisitor访问字节码文件
第二个参数parsingOptions 可选值有以下5个,会对ClassVisitor的visit行为造成不同的影响
0:生成所有ASM代码ClassReader.SKIP_CODE:忽略代码信息,如visitXxxInsn调用ClassReader.SKIP_DEBUG:忽略调试信息,如visitParameter、visitLineNumber、visitLocalVariableClassReader.SKIP_FRAMES:忽略frame信息,如visitFrameClassReader.EXPAND_FRAMES:对frame信息进行扩展
使用ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES能得到功能完整,复杂度低的字节码文件
修改字节码文件的流程如下:

ClassReader是旧字节码的入口,ClassWriter是新字节码的出口,中间可以有多个ClassVisitor来修改字节码。代码结构如下:
ClassVisitor是抽象类,将上面的cv替换为我们自定义的ClassVisitor子类即可
Best Practice
Modify Class Meta Data
重写visit方法
修改了visit的interfaces参数,同样其他参数也可以修改。如修改Java版本version,类名name,父类superName
Modify Class Field
重写visitField方法
Remove Field
正常情况下ClassVisitor#visitField会返回一个FieldVisitor对象,最后会调用其fv.visitEnd,返回null就断掉了
Add Field
同样,想要添加新字段,只需再调用一次ClassVisitor#visitField,再调用fv.visitEnd
但需要思考一下在哪里进行字段插入的操作。
一个类有几个字段就会执行几次visitField,若要在这里插入还需设置一个全局标志位来判断新字段是否插入,否则后面的visitField会造成重复插入。
visitEnd最后调用一次,是个不错的选择,就在这插入新字段。
Modify Class Method
重写MethodDelVisitor方法
Remove Method
和上面删除字段的思路一样。
Add Method
Update Method
enter & exit
如何在方法进入和退出时添加一些逻辑呢?
回想MethodVisitor的调用顺序
visitCode(方法体开始) -> visitXxxIns(方法体) -> visitMaxs -> visitEnd
方法进入时的逻辑可以在visitCode处添加,此时尚未进入方法体。但注意调用visitMaxs时已经退出方法体了,可能执行了return或throw异常,两种情况都是通过visitInsn(opcode)实现的,所以方法退出的逻辑可以在visitInsn处添加。
visitMethod中判断当前方法名和方法描述符是否为目标方法,是则返回自定义的MethodVisitor。visitInsn判断当前opcode是否为throw或return
AdviceAdapter
ASM提供了一个抽象类来实现在方法进入前后添加逻辑,它有两个方法onMethodEnter和onMethodExit
The End
暂时到这吧,后面有需要再回过来补充。
Last updated
Was this helpful?