ASM 3.0
Java 字节码引擎库
1简介
1.1动机
程序分析,生成以及转换是很有用的技术手段,可以应用在很多场景下:
- 程序分析,涉及的范围很广,从简单的语法解析到完整的语义分析,也可用来发现程序中潜在的bug,检测未使用的代码,以及反向工程等。
- 用来帮助编译器生成代码。包括传统的编译器,用在分布式编程中的内嵌的编译器,以及即时编译器等。
- 程序转换可以用来优化程序或者对程序进行混淆,或者在应用中插入调试代码或者性能监控代码,再或者面向切面编程等。
所有的这些技术可以用在任何编程语言中,只是针对不同的编程语言,实现的难度也不尽相同。对应java语言,这些技术可以用在java源代码或者编译过的java类文件上。能够对编译过的java类文件进行处理,有一个明显的优势,那就是不需要源代码。程序转换也因此可以使用在任何应用程序上,包括闭源的和商业的。针对编译后的代码的处理,另外一个优势就是可以在运行时,也就是在这些代码被加载进入虚拟机之前(在运行时生成代码并编译源代码是可能的,但是速度很慢,并且需要一个全功能的java编译器),对类文件进行分析、生成或者转换。对用户而言,这些优势就是内嵌编译器或者切面编程对用户是不可见的。
由于程序分析、生成和转换存在很多应用需求,所以人们针对不同的编程语言,这也包括java,实现了很多工具来进行程序分析、生成和转换。ASM就是这些工具中的一员,其主要是面向java。ASM被设计用以在运行时对java类进行生成和转换,当然也包括离线处理。ASM库主要是工作在编译好的java类之上。ASM被设计的短小精悍,尽量保证其速度很快,同时库的容量又很小。ASM快速的目标就是避免在运行时动态生成class和转换对应用程序的速度影响。由于ASM库容量很小,因此,它可以被用在很多内存受限的环境中,同时避免导致小应用程序或者库的容量变大。
ASM不仅仅是生成和转换编译后的java类的工具,而且它也是最有效的。它可以从http://asm.objectweb.org上下载得到。它的主要优势包括如下几方面:
- 它有一个很小,但是设计良好并且模块化的API,且易于使用。
- 它具有很好的文档,并且还有eclipse插件。
- 它支持最新的java版本,java 6。
- 它很小,很快,很健壮。
- 它有一个很大的用户社区,可以给新用户提供支持。
- 它的开原许可允许你几乎以任何方式来使用它。
1.2概述
1.2.1范围
ASM库的目标是生成、转换和分析编译后的java类,在虚拟机中以字节数组表示(它们是保存在硬盘上,在运行的时候被加载入java虚拟机)。基于这个目标,ASM提供了对这些字节数组进行读取,写入和转换的工具,这些工具使用更高级别的概念,而非字节,比如数字常量,字符串,java标识符,java类型,java类结构元素等等。注意,ASM库的范围严格限于对java类进行读取,写入,转换以及分析。Java类文件的加载过程超出了该范围。
1.2.2模型
ASM库提供了两套API用来生成、转换编译后的java类:核心的API是基于事件的,而Tree API是基于对象的。
在基于事件的模型中,一个java类表现为一系列事件,每一个事件代表了类中的一个元素,如类的头部,字段,方法声明,或者指令等。基于事件的API定义了一系列的事件以及这些事件发生的顺序,并且提供了一个类解析器,这个解析器在解析到一个类元素时,就会生成一个事件,与此对应的,类写入器在发生这些事件的时候生成编译后的类。
在基于对象的模型中,一个java类表现为一个对象树,每一个对象代表了类中的一部分,入类本身,一个字段,一个方法或者指令等。每个对象都具有指向其组成成分的引用。基于对象的模型提供了一种方式,用来将代表类的一系列事件转换为一颗对象树,反之亦然,将一颗对象树转换为一系列对等事件。换句话说,基于对象的模型是构建在基于事件的模型之上。
这两套API可以和java中的XML解析库SAX(the Simple API for XML)和DOM(Document Object Model) API来对比。基于事件的API类似于SAX,而基于对象的API类似于DOM。基于对象的API构建在基于事件的API之上,类似于DOM可以在SAX之上使用。
ASM之所以提供了两套API,那是因为没有最好的API,这两套API具有它的优势和缺点:
- 基于事件的API速度更快,需要的内存空间要小于基于对象的API,因为没必要在内存中保留一个代表类结构的对象树(这个不同同时也存在于SAX和DOM之间)。
- 但是,通过基于事件的API来实现类的转换比较困难,因为在给定的时间,只有一个与该事件对应的元素可用。但是,基于对象的API能够在内存中保留整个类。
注意,这两套API在一个时刻都只能处理一个类,并且不依赖于其他类:因为没有维护关于类结构的信息,因此如果一个类的转换影响了其他类,那么由用户来决定是否修改其他类。
1.2.3架构
ASM应用有一个健壮的架构。基于事件的API,在事件的产生者(类解析器)的周围,是事件的消费者(类写入器),以及一些预定义的事件过滤器,用户自定义的事件产生者、消费者和过滤器都可以被添加进去。可以按照以下的两个步骤来使用API:
- 将事件的产生者、过滤器和消费者组件放置到架构的合适位置,
- 然后,启动事件的产生者进行类生产或者转换。
基于对象模型的API也有其架构:操作对象的类生成器和转换器可以被组合起来使用,它们之间的链接代表了转换的顺序。
尽管在典型的ASM应用中,大部分组件架构都是很简单的,但是可以想象一下像下面这幅图的复杂架构,箭头代表在链条中,基于事件或者对象模型中的类解析器、写入器和转换器之间的通信,在基于事件和对象模型之间需要经过合适的转换。
1.3组织
ASM库分为几个包,并且在不同的jar包中分发:
- org.objectweb.asm 和 org.objectweb.asm.signature 包定义了基于事件的API,并且提供了类解析组件和写入组件。它们包含在asm.jar归档中。
- org.objectweb.asm.util包,包含在asm-util.jar归档中,提供了一些基于核心API的工具,可以用来协助开发和调试ASM应用。
- org.objectweb.asm.commons包提供了几个预定义类转换组件,大部分基于核心API,它包含在asm-common.jar归档中。
- org.objectweb.asm.tree包,包含在asm-tree.jar归档中,定义了基于对象模型的API,同时也提供了一些工具对基于事件的和基于对象模型的展现进行转换。
- org.objectweb.asm.tree.analysis包提供了一个类分析框架,以及几个预定义好的类分析器,基于tree API。它包含在asm-analysis.jar归档中。
文档被组织为两部分,第一部分,覆盖了核心API,如asm,asm-util和asm-commons归档。第二部分覆盖了tree API(基于对象模型的API),如asm-tree和asm-analysis归档。每部分包含至少一章用来介绍与类相关的API内容,一章与方法相关的API,一章与注解、泛型等相关的API。每章都讲解了编程接口,以及相关的工具,和预定义好的组件等。所有这些例子的源代码可在该地址下载:http://asm.objectweb.org/。
字体约定
Italic斜体 用来强调句子中的元素。
等宽字体 表示代码片段
等宽粗体 表示强调代码元素
Italic等宽斜体 表示代码中的变量部分和标签
1.4致谢
我非常感谢François Horn 在该文档拟定过程中的珍贵的评论,这帮助改善了文档的结构和可读性。