ASM 是一个通用的Java字节码操作和分析框架。
它可以用来修改现有的类或动态地生成类,直接以二进制形式。
ASM提供了一些通用的字节码转换和分析算法,可以从这些算法中构建自定义复杂的转换和代码分析工具。
ASM提供与其他Java字节码框架类似的功能,但主要关注性能。
因为它的设计和实现都尽可能地小和快,所以非常适合在动态系统中使用(当然也可以以静态的方式使用,例如在编译器中)。
ASM 是一个通用的Java字节码操作和分析框架。
它可以用来修改现有的类或动态地生成类,直接以二进制形式。
ASM提供了一些通用的字节码转换和分析算法,可以从这些算法中构建自定义复杂的转换和代码分析工具。
ASM提供与其他Java字节码框架类似的功能,但主要关注性能。
因为它的设计和实现都尽可能地小和快,所以非常适合在动态系统中使用(当然也可以以静态的方式使用,例如在编译器中)。
程序分析,生成和转换是有用的技术,可以在许多情况下使用:
范围从简单的语法分析到完整的语义分析,可用于查找应用程序中的潜在错误,检测未使用的代码,反向工程代码等。
其中包括传统的编译器,还有用于分布式编程的存根或骨架编译器,即时编译器等
将调试或性能监控代码插入应用程序,用于面向方面的编程等
所有这些技术都可以用于任何编程语言,但这或多或少容易实现,具体取决于语言。
对于Java,它们可以在Java源代码或已编译的Java类上使用。
所谓 Java 类文件,就是通常用 javac 编译器产生的 .class 文件。
这些文件具有严格定义的格式。
为了更好的理解 ASM,首先对 Java 类文件格式作一点简单的介绍。
Java 源文件经过 javac 编译器编译之后,将会生成对应的二进制文件(如下图所示)。
用于生成和转换已编译类的ASM API基于ClassVisitor抽象类(请参见图2.4)。
此类中的每个方法都对应于同名的类文件结构部分(请参见图2.1)。
通过单个方法调用可以访问简单部分,该方法的参数描述其内容,并返回void。
可以通过返回辅助访问者类的初始方法调用来访问其内容可以具有任意长度和复杂度的节。
visitAnnotation,visitField和visitMethod方法就是这种情况,它们分别返回AnnotationVisitor,FieldVisitor和MethodVisitor。
除了ClassVisitor类以及相关的ClassReader和ClassWriter组件之外,ASM在org.objectweb.asm.util包中还提供了一些工具,这些工具在类生成器或适配器的开发过程中很有用,但不需要 在运行时。
ASM还提供了一个实用程序类,用于在运行时处理内部名称,类型描述符和方法描述符。
所有这些工具在下面介绍。
如您在前几节中所见,ASM API公开了Java类型,因为它们存储在编译的类中,即作为内部名称或类型描述符。
可以将它们公开显示在源代码中,以使代码更具可读性。
本章说明如何使用核心ASM API生成和转换编译方法。
它首先介绍了已编译的方法,然后提供了许多说明性示例,介绍了相应的ASM接口,组件以及生成和转换它们的工具。
在已编译的类中,方法的代码存储为一系列字节码指令。
为了生成和转换类,了解这些指令并理解它们的工作原理至关重要。
本节概述了这些指令,这些指令应足以开始对简单的类生成器和转换器进行编码。
要获得完整的定义,您应该阅读Java虚拟机规范。
在提供字节码指令之前,必须提供Java虚拟机执行模型。
用于生成和转换已编译方法的ASM API基于MethodVisitor抽象类(请参见图3.4),该类由ClassVisitor的visitMethod方法返回。
除了下一章中将介绍的与注释和调试信息有关的一些方法外,此类还基于以下内容为每个字节码指令类别定义了一个方法:
这些指令的参数数量和类型(这些类别与3.1.2节中介绍的类别不对应)。
必须按以下顺序调用这些方法(在MethodVisitor接口的Javadoc中指定了一些其他约束):
visitAnnotationDefault?
( visitAnnotation | visitParameterAnnotation | visitAttribute )*
( visitCode
( visitTryCatchBlock | visitLabel | visitFrame | visitXxxInsn |
visitLocalVariable | visitLineNumber )*
visitMaxs )?
visitEnd
org.objectweb.asm.commons 软件包包含一些预定义的方法适配器,这些适配器可用于定义您自己的适配器。
本节介绍其中的三个,并说明如何将它们与3.2.4节的AddTimerAdapter示例一起使用。
它还显示了如何使用上一章中介绍的工具简化方法的生成或转换。
2.3 节中介绍的工具也可以用于方法。
许多字节码指令(例如xLOAD,xADD或xRETURN)取决于它们所应用的类型。
Type类提供了一个getOpcode方法,对于这些指令,该方法可用于获取与给定类型相对应的操作码。
上面几节谈论了大量的 method 方法,感觉 ASM 提供了很多强大的功能。
但是缺少实战有时候就比较没有实感,所以选择一个比较简单的例子进行编码。
可以基于 asm 获取 class 的基本信息
可以学习 ReflectASM 的思想,自己基于 ASM 实现 field/method 等较为高效的调用。
直接可以对已有的方法进行增强。
换种方式就是直接生成当前类的代码,对代码进行增强。
本章说明如何使用核心API生成和转换已编译的Java类元数据,例如注解。
每个部分均以一种类型的元数据开始呈现,然后以一些说明性示例呈现相应的ASM接口,组件和工具以生成和转换这些元数据。
诸如 List
之类的通用类以及使用它们的类包含有关它们声明或使用的通用类型的信息。
字节码指令在运行时不使用此信息,但是可以通过反射API进行访问。
编译器还使用它进行单独的编译。
出于向后兼容性的原因,关于泛型类型的信息不是存储在类型或方法描述符中(它们在Java 5中引入泛型之前就已定义),而是存储在称为类型,方法和类签名的类似构造中。