概述
ASM是Java中比较流行的用来读写字节码的类库,用来基于字节码层面对代码进行分析和转换。在读写的过程中可以加入自定义的逻辑以增强或修改原来已编译好的字节码,比如CGLIB用它来实现动态代理。ASM被设计用于在运行时对Java类进行生成和转换,当然也包括离线处理。ASM短小精悍、且速度很快,从而避免在运行时动态生成字节码或转换时对程序速度的影响,又因为它体积小巧,可以在很多内存受限的环境中使用。
ASM的主要优势包括如下几个方面:
1. 它又一个很小,但设计良好并且模块化的API,且易于使用。
2. 它具有很好的文档,并且还有eclipse插件。
3. 它支持最新的Java版本。
4. 它短小精悍、快速、健壮。
5. 它又一个很大的用户社区,可以给新用户提供支持。
6. 它的开源许可允许你几乎以任何方式来使用它。
关于ASM的详细文档可以参考:ASM 3.0:Java字节码引擎库,写的很详细的一个文档。
ASM Core设计一览
在ASM的核心实现中,它主要有以下几个类、接口(在org.objectweb.asm包中):
ClassReader类:字节码的读取与分析引擎。它采用类似SAX的事件读取机制,每当有事件发生时,调用注册的ClassVisitor、AnnotationVisitor、FieldVisitor、MethodVisitor做相应的处理。
ClassVisitor接口:定义在读取Class字节码时会触发的事件,如类头解析完成、注解解析、字段解析、方法解析等。
AnnotationVisitor接口:定义在解析注解时会触发的事件,如解析到一个基本值类型的注解、enum值类型的注解、Array值类型的注解、注解值类型的注解等。
FieldVisitor接口:定义在解析字段时触发的事件,如解析到字段上的注解、解析到字段相关的属性等。
MethodVisitor接口:定义在解析方法时触发的事件,如方法上的注解、属性、代码等。
ClassWriter类:它实现了ClassVisitor接口,用于拼接字节码。
AnnotationWriter类:它实现了AnnotationVisitor接口,用于拼接注解相关字节码。
FieldWriter类:它实现了FieldVisitor接口,用于拼接字段相关字节码。
MethodWriter类:它实现了MethodVisitor接口,用于拼接方法相关字节码。
SignatureReader类:对类定义、字段定义、方法定义、本地变量定义的签名的解析。Signature因范型引入,用于存储范型定义时的元数据(因为这些元数据在运行时会被擦除)。
SignatureVisitor接口:定义在解析Signature时会触发的事件,如正常的Type参数、类或接口的边界等。
SignatureWriter类:它实现了SignatureVisitor接口,用于拼接范型相关字节码。
Attribute类:字节码中属性的类抽象。
ByteVector类:字节码二进制存储的容器。
Opcodes接口:字节码指令的一些常量定义。
Type类:类型相关的常量定义以及一些基于其上的操作。
他们之间的类图关系如下:
ClassReader实现
ClassReader是ASM中最核心的实现,它用于读取并解析Class字节码。类字节码格式可以具体参考:《Java字节码格式详解1》、《Java字节码格式详解2》、《Java字节码格式详解3》
在构建ClassReader实例时,它首先保存字节码二进制数组b,然后创建items数组,数组的长度在字节码数组的第8、9个字节指定(最前面4个字节是魔数CAFEBABE,之后2个字节是次版本号,再后2个字节是主版本号),每个item表示常量池项在字节码数组的偏移量加1(常量池中每个项由1个字节的type和紧跟的字节数组表示,常量池项有12种类型,其中CONSTANT_FieldRef_Info、CONSTANT_MethodRef_Info、CONSTANT_InterfaceMethodRef_Info、CONSTANT_NameAndType_Info包括其类型字节占用5个字节,另外4个字节每2个字节为字段、方法等所在的类、其名称、描述符在当前常量池中CONSTANT_Utf8_Info类型的引用;CONSTANT_Integer_Info、CONSTANT_Float_Info包括其类型字节占用5个字节,另外四个字节为其对应的值;CONSTANT_Class_Info、CONSTANT_String_Info包括其类型字节占用3个字节,另外两个字节为在当前常量池CONSTANT_Utf8_Info项的索引;CONSTANT_Utf8_Info类型第1个字节表示类型,第2、3个字节为该项所表示的字符串的长度);CONSTANT_Double_Info、CONSTANT_Long_Info加类型字节为9个字;maxStringLength表示最长的UTF8类型的常量池项的值,用于决定在解析CONSTANT_Utf8_Info类型项时最大需要的字符数组;header表示常量池之后的字节码的第一个字节。
在调用ClassReader的accept方法时,它解析字节码中常量池之后的所有元素。紧接着常量池的2个字节是该类的access标签:ACC_PUBLIC、ACC_FINAL等;之后2个字节为当前类名在常量池CONSTANT_Utf8_Info类型的索引;之后2个字节为其父类名在常量池CONSTANT_Utf8_Info类型的索引(索引值0表示父类为null,即直接继承自Object类);再之后为其实现的接口数长度和对应各个接口名在常量池中CONSTANT_Utf8_Info类型的索引值;暂时先跳过Field和Method定义信息,解析类的attribute表,它用两个字节表达attribute数组的长度,每个attribute项中最前面2个字节是attribute名称:SourceFile(读取sourceFile值)、InnerClasses(暂时纪录起始索引)、EnclosingMethod(纪录当前匿名类、本地类包含者类名以及包含者的方法名和描述符)、Signature(类的签名信息,用于范型)、RuntimeVisibleAnnotations(暂时纪录起始索引)、Deprecated(表识属性)、Synthetic(标识属性)、SourceDebugExtension(为调试器提供的自定义扩展信息,读取成一个字符串)、RuntimeInvisibleAnnotations(暂时纪录起始索引),对其他不识别的属性,纪录成Attribute链,如果attribute名称符合在accept中attribute数组中指定的attribute名,则替换传入的attribute数组对应的项;根据解析出来的信息调用以下visit方法:
// sourceFile, sourceDebug
void visitSource(String source, String debug);
// EnclosingMethod attribute: enclosingOwner, enclosingName, enclosingDesc.
// Note: only when the class has EnclosingMethod attribute, meaning the class is a local class or an anonymous class
void visitOuterClass(String owner, String name, String desc);
依次解析RuntimeVisibleAnnotations和RuntimeInvisibleAnnotations属性,首先解析定义的Annotation的描述符以及运行时可见flag,返回用户自定义的AnnotationVisitor:
对每个定义的Annotation,解析其键值对,并根据不同的Annotation字段值调用AnnotationVisitor中的方法,在所有解析结束后,调用AnnotationVisitor.visitEnd方法:
// 对基本类型的数组,依然采用该方法,visitArray只是在非基本类型时调用。
void visit(String name, Object value);
void visitEnum(String name, String desc, String value);
AnnotationVisitor visitAnnotation(String name, String desc);
AnnotationVisitor visitArray(String name);
void visitEnd();
}
之前解析出的attribute链表(非标准的Attribute定义),对每个Attribute实例,调用ClassVisitor中的visitAttribute方法:
Attribute类包含type字段和一个字节数组:
public final String type;
byte[] value;
Attribute next;
}
对每个InnerClasses属性,解析并调用ClassVisitor的visitInnerClass方法(该属性事实上保存了所有其直接内部类以及它本身到最顶层类的路径):
解析字段,它紧跟接口数组定义之后,最前面的2个字节为字段数组的长度,对每个字段,前面2个字节为访问flag定义,再后2个字节为Name索引,以及2个字节的描述符索引,然后解析其Attribute信息:ConstantValue、Signature、Deprecated、Synthetic、RuntimeVisibleAnnotations、RuntimeInvisibleAnnotations以及非标准定义的Attribute链,而后调用ClassVisitor的visitField方法,返回FieldVisitor实例:
FieldVisitor visitField(int access, String name, String desc, String signature, Object value);
对返回的FieldVisitor依次对其Annotation以及非标准Attribute解析,调用其visit方法,并在完成后调用它的visitEnd方法:
AnnotationVisitor visitAnnotation(String desc, boolean visible);
void visitAttribute(Attribute attr);
void visitEnd();
}
解析方法定义,它紧跟字段定义之后,最前面的2个字节为方法数组长度,对每个方法,前面2个字节为访问flag定义,再后2个字节为Name索引,以及2个字节的方法描述符索引,然后解析其Attribute信息:Code、Exceptions、Signature、Deprecated、RuntimeVisibleAnnotations、AnnotationDefault、Synthetic、RuntimeInvisibleAnnotations、RuntimeVisibleParameterAnnotations、RuntimeInvisibleParameterAnnotations以及非标准定义的Attribute链,如果存在Exceptions属性,解析其异常类数组,之后调用ClassVisitor的visitMethod方法,返回MethodVisitor实例:
AnnotationDefault为对Annotation定义时指定默认值的解析;然后依次解析RuntimeVisibleAnnotations、RuntimeInvisibleAnnotations、RuntimeVisibleParameterAnnotations、RuntimeInvisibleParameterAnnotations等属性,调用相关AnnotationVisitor的visit方法;对非标准定义的Attribute链,依次调用MethodVisitor的visitAttribute方法:
AnnotationVisitor visitAnnotationDefault();
AnnotationVisitor visitAnnotation(String desc, boolean visible);
AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible);
void visitAttribute(Attribute attr);
}
对Code属性解析,读取2个字节的最深栈大小、最大local变量数、code占用字节数,调用MethodVisitor的visitCode()方法表示开始解析Code属性,对每条指令,创建一个Label实例并构成Label数组,解析Code属性中的异常表,对每个异常项,调用visitTryCatchBlock方法:
Label包含以下信息:
* A label represents a position in the bytecode of a method. Labels are used
* for jump, goto, and switch instructions, and for try catch blocks.
*
* @author Eric Bruneton
*/
public class Label {
public Object info;
int status;
int line;
int position;
private int referenceCount;
private int[] srcAndRefPositions;
int inputStackTop;
int outputStackMax;
Frame frame;
Label successor;
Edge successors;
Label next;
}
解析Code属性中的内部属性信息:LocalVariableTable、LocalVariableTypeTable、LineNumberTable、StackMapTable、StackMap以及非标准定义的Attribute链,对每个Label调用其visitLineNumber方法以及对每个Frame调用visitFrame方法,并且对相应的指令调用相应的方法:
// Visits a zero operand instruction.
void visitInsn(int opcode);
// Visits an instruction with a single int operand.
void visitIntInsn(int opcode, int operand);
// Visits a local variable instruction. A local variable instruction is an instruction that loads or stores the value of a local variable.
void visitVarInsn(int opcode, int var);
// Visits a type instruction. A type instruction is an instruction that takes the internal name of a class as parameter.
void visitTypeInsn(int opcode, String type);
// Visits a field instruction. A field instruction is an instruction that loads or stores the value of a field of an object.
void visitFieldInsn(int opcode, String owner, String name, String desc);
// Visits a method instruction. A method instruction is an instruction that invokes a method.
void visitMethodInsn(int opcode, String owner, String name, String desc);
// Visits a jump instruction. A jump instruction is an instruction that may jump to another instruction.
void visitJumpInsn(int opcode, Label label);
// Visits a label. A label designates the instruction that will be visited just after it.
void visitLabel(Label label);
// Visits a LDC instruction.
void visitLdcInsn(Object cst);
// Visits an IINC instruction.
void visitIincInsn(int var, int increment);
// Visits a TABLESWITCH instruction.
void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels);
// Visits a LOOKUPSWITCH instruction.
void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels);
// Visits a MULTIANEWARRAY instruction.
void visitMultiANewArrayInsn(String desc, int dims);
// Visits a try catch block.
void visitTryCatchBlock(Label start, Label end, Label handler, String type);
void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index);
// Visits a line number declaration.
void visitLineNumber(int line, Label start);
// Visits the maximum stack size and the maximum number of local variables of the method.
void visitMaxs(int maxStack, int maxLocals);
最后调用ClassVisitor的visitEnd方法:
ClassWriter实现
ClassWriter继承自ClassVisitor接口,可以使用它调用其相应的visit方法动态的构造一个字节码类。它包含以下字段信息:
//The class reader from which this class writer was constructed, if any.
ClassReader cr;
//Minor and major version numbers of the class to be generated.
int version;
//Index of the next item to be added in the constant pool.
int index;
//The constant pool of this class.
final ByteVector pool;
//The constant pool's hash table data.
Item[] items;
//The threshold of the constant pool's hash table.
int threshold;
//A reusable key used to look for items in the {@link #items} hash table.
final Item key;
//A reusable key used to look for items in the {@link #items} hash table.
final Item key2;
//A reusable key used to look for items in the {@link #items} hash table.
final Item key3;
//A type table used to temporarily store internal names that will not necessarily be stored in the constant pool.
Item[] typeTable;
//Number of elements in the {@link #typeTable} array.
private short typeCount;
//The access flags of this class.
private int access;
//The constant pool item that contains the internal name of this class.
private int name;
//The internal name of this class.
String thisName;
//The constant pool item that contains the signature of this class.
private int signature;
//The constant pool item that contains the internal name of the super class of this class.
private int superName;
// Number of interfaces implemented or extended by this class or interface.
private int interfaceCount;
//The interfaces implemented or extended by this class or interface.
private int[] interfaces;
//The index of the constant pool item that contains the name of the source file from which this class was compiled.
private int sourceFile;
//The SourceDebug attribute of this class.
private ByteVector sourceDebug;
//The constant pool item that contains the name of the enclosing class of this class.
private int enclosingMethodOwner;
//The constant pool item that contains the name and descriptor of the enclosing method of this class.
private int enclosingMethod;
//The runtime visible annotations of this class.
private AnnotationWriter anns;
//The runtime invisible annotations of this class.
private AnnotationWriter ianns;
//The non standard attributes of this class.
private Attribute attrs;
//The number of entries in the InnerClasses attribute.
private int innerClassesCount;
//The InnerClasses attribute.
private ByteVector innerClasses;
//The fields of this class. These fields are stored in a linked list of {@link FieldWriter} objects, linked to each other by their {@link FieldWriter#next} field. This field stores the first element of this list.
FieldWriter firstField;
//This field stores the last element of this list.
FieldWriter lastField;
//The methods of this class. These methods are stored in a linked list of {@link MethodWriter} objects, linked to each other by their {@link MethodWriter#next} field. This field stores the first element of this list.
MethodWriter firstMethod;
//This field stores the last element of this list.
MethodWriter lastMethod;
//true if the maximum stack size and number of local variables must be automatically computed.
private final boolean computeMaxs;
//true if the stack map frames must be recomputed from scratch.
private final boolean computeFrames;
//true if the stack map tables of this class are invalid.
boolean invalidFrames;
}
相关推荐
4. **ClassWriter**:它实现了ClassVisitor接口,用于构建新的字节码。当你需要生成或修改字节码时,可以使用ClassWriter将处理后的结果输出为字节码形式。 5. **AnnotationWriter**、**FieldWriter** 和 **...
`ClassReader`、`ClassVisitor`和`ClassWriter`是ASM Core API中的三大关键类。 `ClassReader`负责读取已有的 `.class` 文件,解析其内部结构,而`ClassWriter`则负责将修改后的类信息写回。`ClassVisitor`作为它们...
1. **ClassWriter**: 这个类负责生成字节码,它是ASM的核心组件之一。通过向ClassWriter发送指令,我们可以构建出完整的类结构,包括类名、父类、接口、字段和方法。 2. **ClassReader**: 这个类用于读取已有的字节...
asm-3.3.1.jar是一个Java字节码操纵框架,它可以在二进制形式下动态生成stub类或者其他代理类,同时...为了实现这些功能,它定义了一些重要的类,如ClassReader、ClassVisitor和ClassWriter等,这些都位于Core API中。
在这个包里,你可以找到一些重要的类,如ClassReader、ClassVisitor和ClassWriter等,它们都是ASM库的核心API。 除此之外,ASM库也提供了一些其他的功能。例如,它可以生成二进制的class文件,也可以在类被加载入...
ASM 通过 ClassReader 类读取 .class 文件,然后通过 ClassWriter 类生成字节码。在这个过程中,可以使用 ClassAdapter 类适配器来拦截和修改 ClassReader 和 ClassWriter 之间的交互。开发者可以通过继承 Class...
3. 访问和修改类:ASM提供了ClassReader和ClassVisitor接口,可以用来读取和修改已有的类结构,包括字段、方法、注解等。 4. 写入类:生成的字节码可以通过ClassWriter写入到新的Class文件中,或者通过自定义...
这包括了用于表示类的ClassReader、ClassWriter、ClassVisitor等接口和组件。 3. Tools(工具):ASM提供了一系列工具,包括Type工具用于处理类型描述符,TraceClassVisitor工具用于跟踪ClassVisitor的执行过程,...
ASM提供了两种主要的API:ClassWriter用于生成字节码,而ClassReader则用于解析已存在的字节码。 2. **ASM的使用场景** - **动态代理**:通过ASM,开发者可以创建动态代理类,实现运行时接口的动态实现。 - **...
6. **ClassReader和ClassWriter**:这两个核心组件负责读取和写入类文件,是ASM Commons的基础。 压缩包内还包括了"asm-2.2.1.jar.license.txt"文件,这通常是开源软件的许可协议文本,用于说明ASM Commons库的使用...
2. 类的生成:通过ClassWriter类,ASM允许我们构建新的类或修改已有的类字节码。 3. 访问控制器:ASM的访问控制器如ClassVisitor、MethodVisitor和FieldVisitor,提供了访问和修改类、方法和字段的API。 4. 字节码...
ASM的API主要分为两个部分:`ClassReader`和`ClassWriter`。`ClassReader`用于读取和解析类文件,而`ClassVisitor`接口则定义了访问和修改类结构的方法。开发者可以创建自己的`ClassVisitor`子类,实现所需的操作。`...
它包括了几个关键类,如`ClassReader`用于读取已编译的类,`ClassWriter`用于生成新的类或修改现有类的字节码,还有`ClassVisitor`、`MethodVisitor`等接口,它们允许开发者以访问者模式来遍历和操作类结构。ASM的...
4. **生成新的字节码**:在访问者遍历完所有方法后,使用`ClassWriter`生成修改后的字节码。 5. **写入字节码**:最后,将新生成的字节码写入到新的.class文件中,或者直接在内存中加载。 在实际应用中,Method ...
例如,ASM提供了`ClassReader`类来读取和解析.class文件,`ClassWriter`类来生成新的字节码,以及一系列的访问器(如`ClassVisitor`、`FieldVisitor`和`MethodVisitor`)来遍历和修改类结构。 `asm-2.2.1.jar....
- 类表示:包括 `ClassReader`、`ClassWriter` 和 `ClassVisitor` 等接口,用于分析、生成和修改类结构。 - 字段和方法表示:包括 `FieldVisitor` 和 `MethodVisitor` 等接口,用于访问和操作类的字段和方法。 - ...
ASM提供了ClassReader和ClassWriter两个核心类,前者用于读取和解析.class文件,后者则负责生成新的字节码。中间过程可以通过ClassAdapter进行自定义的处理。主要API包括: - `ClassReader`:读取并解析.class文件...
它包括`ClassReader`和`ClassWriter`两个关键类。`ClassReader`用于读取类文件,提供了一个`accept`方法,可以接受一个`ClassVisitor`对象,将类的信息传递给它。`ClassWriter`则用于创建新的类或修改已有的类。 2....
2. **ClassWriter**: 生成 `.class` 文件的类输出器,可以基于 ClassReader 提供的信息。 3. **ClassAdapter**: 适配器类,提供了一种简单的方式来扩展 ClassVisitor,允许开发者通过重写部分方法来实现自定义的行为...