下面三篇文章讲解怎么定义类、方法、变量等说的很详细 明了。
http://alvinqq.iteye.com/blog/940960
http://alvinqq.iteye.com/blog/940965
http://alvinqq.iteye.com/blog/940970
一、如何使用ASM
ASM提供了两套API供使用者使用,一套叫Core API,是基于事件的方式对字节码进行处理;另一套叫Tree API,是基于对象的方式对字节码进行处理。如果你熟悉XML解析,那么实际上Core API就是SAX这种处理模式,而Tree API就是DOM这种处理模式。
二、Core API
使用Core API进行对字节码进行处理一般需要三个部分:
- 一个事件的生产者,用于产生各种事件,通常这会是一个
ClassReader
- 一个事件的消费者,用于消费各种事件,通常这会是一个
ClassWriter
- 若干个事件的过滤器,这些过滤器可以对感兴趣的事件进行过滤来处理,这通常会是一些
ClassVisitor
其处理过程如下图所示:
从文件流中读入一个待处理的Class文件,然后new了ClassReader
,作为事件源,然后new了一个ClassWriter
,作为事件的接收者,还实现了一个ClassVisitor
,这个ClassVisitor
将所有的public方法变成了private的方法。接着通过调用cr.accept
方法来触发事件,通过cw.toByteArray
来拿到处理后的字节码并且输出到文件。
从前面的图可以看出,采用Core API处理字节码,其实就是通过继承ClassVisitor
,并且覆盖ClassVisitor
中的对应的方法来对特定的事件进行处理的过程,其实这里的事件基本上对应到了Class文件中的各个部分,除了常量池部分,所以如果了解了Class文件的结构,那么用Core API处理起来应该得心应手。
除了ClassVisitor
之外,Core API还提供了MethodVisitor
、FieldVisitor
、AnnotationVisitor
来对方法,字段和注解操作。
三、 Tree API
Tree API是基于对象的方式来处理字节码,Tree API的最核心的一个类就是ClassNode
,它就代表了一个Java Class文件,它里面的属性对应到了一个Class文件的各个部分。
ClassNode,用来创建一个继承了java.lang.Runnable
接口的接口ASMInterface
,它包含了一个方法public void stop()
,最后将生成的字节码通过ClassWriter
输出到文件。
和创建类一样,通过Tree API修改一个类也只需要修改ClassNode
的属性。如果你要修改方法,字段或者注解,那么可以通过ClassNode
拿到MethodNode
、FieldNode
、AnnotationNode
来进行对应的修改。
四、什么时候使用Core API,什么时候使用Tree API?
Core API和Tree API其实各有优缺点:
- Core API的优势是处理速度快,占用内存小,因为它不需要在内存中将整个Class文件表示出来,缺点是基于事件的方式处理,如果错过一个事件,那么就是过了这个村,没有这个店了,这样如果需要实现诸如将特定
GOTO
出插入其他指令,就会比较麻烦,因为GOTO
可以跳转到之前的指令,但是之前的指令的事件已经被处理了,到时候只能再次触发一遍事件来处理。 - Tree API的优势就是Core API的劣势,对于上面提到的
GOTO
的这种情况,Tree API处理起来就轻松了很多,因为在内存中有Class文件的完整表示,随便什么样的顺序去改都是没有问题的。缺点就是占用内存比较大,处理速度比较慢。
如果你查看ClassNode
的源代码,那么可以发现ClassNode
事实上继承了ClassVisitor
。那么,我们就可以将在实际操作的时候将Core API和Tree API结合起来,灵活运用各自的优缺点去解决问题。
五、辅助工具类
ASM除了提供了Core API和Tree API两套API以外,还提供了几个比较实用的工具类
CheckClassAdapter
实际上,用ASM生成的字节码可能并不符合Java虚拟机规范的,如果需要检查生成的字节码符不符合规范,那么可以用CheckClassAdapter
作为一个ClassVisitor
加入到ClassVisitor
链中,如果字节码不符合规范,那么CheckClassAdapter
就会抛出异常。
ASMifier
ASM作为一个字节码操作工具,相对于其他的字节码操作工具,比如Javassist,写起来还是比较烦琐的,如果你已经有了一个Class文件,想要知道如何通过ASM生成这个Class文件,那么就可以直接用ASMifier
这个类,通过这个类,可以直接生成出生成目标类的ASM代码,一定程度上简化了直接手写ASM代码的繁琐工作。
ASMifier可以直接通过命令行来使用,比如那我们刚才生成的那个ASMInterface
为例:
可以看到ASMifier
直接将生成ASMInterface
所需要的ASM代码直接打印出来了。
LocalVariableSorter
假设你要往一个方法里面加入一个本地变量,那么你就需要将这个变量加入到本地变量表的最后,遗憾的是,本地变量表的大小只有当你在调用visitMaxs
的时候才知道,通常,这个时候已经到了方法的结尾处,再想加本地变量已经晚了,现在通过LocalVariableSorter
这个ClassVisitor
,你就可以非常简单插入一个本地变量。
转载:http://www.khotyn.com/2012/03/06/asm_note/