概述
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方法:
void visit(int version, int access, String name, String signature, String superName, String[] interfaces);
// 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:
AnnotationVisitor visitAnnotation(String desc, boolean visible);
對每個定義的Annotation,解析其鍵值對,并根據不同的Annotation字段值調用AnnotationVisitor中的方法,在所有解析結束后,調用AnnotationVisitor.visitEnd方法:
public interface AnnotationVisitor {
// 對基本類型的數組,依然采用該方法,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方法:
void visitAttribute(Attribute attr);
Attribute類包含type字段和一個字節數組:
public class Attribute {
public final String type;
byte[] value;
Attribute next;
}
對每個InnerClasses屬性,解析并調用ClassVisitor的visitInnerClass方法(該屬性事實上保存了所有其直接內部類以及它本身到最頂層類的路徑):
void visitInnerClass(String name, String outerName, String innerName, int access);
解析字段,它緊跟接口數組定義之后,最前面的2個字節為字段數組的長度,對每個字段,前面2個字節為訪問flag定義,再后2個字節為Name索引,以及2個字節的描述符索引,然后解析其Attribute信息:ConstantValue、Signature、Deprecated、Synthetic、RuntimeVisibleAnnotations、RuntimeInvisibleAnnotations以及非標準定義的Attribute鏈,而后調用ClassVisitor的visitField方法,返回FieldVisitor實例:
// 其中value為靜態字段的初始化值(對非靜態字段,它的初始化必須由構造函數實現),如果沒有初始化值,該值為null。
FieldVisitor visitField(int access, String name, String desc, String signature, Object value);
對返回的FieldVisitor依次對其Annotation以及非標準Attribute解析,調用其visit方法,并在完成后調用它的visitEnd方法:
public interface FieldVisitor {
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實例:
MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions);
AnnotationDefault為對Annotation定義時指定默認值的解析;然后依次解析RuntimeVisibleAnnotations、RuntimeInvisibleAnnotations、RuntimeVisibleParameterAnnotations、RuntimeInvisibleParameterAnnotations等屬性,調用相關AnnotationVisitor的visit方法;對非標準定義的Attribute鏈,依次調用MethodVisitor的visitAttribute方法:
public interface MethodVisitor {
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方法:
void visitTryCatchBlock(Label start, Label end, Label handler, String type);
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方法,并且對相應的指令調用相應的方法:
void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack);
// 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方法:
void visitEnd();
ClassWriter實現
ClassWriter繼承自ClassVisitor接口,可以使用它調用其相應的visit方法動態的構造一個字節碼類。它包含以下字段信息:
public class ClassWriter implements ClassVisitor {
//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;
}
posted on 2014-06-25 21:14
DLevin 閱讀(20235)
評論(1) 編輯 收藏 所屬分類:
深入源碼