我们在Java字节码文件结构剖析(二)中己经对MyTest35_1这个类的字节码做了完整的解析,今天,我们来看看Spring的ASM技术是如何来解析Java类字节码的。话不多说,先上实例。
MyTest35_1.java
package com.spring_1_100.test_31_40.test35_resource_inject; public class MyTest35_1 { String str = "Welcome"; private int x = 5; private static Integer in = 10; public static void main(String[] args) { MyTest35_1 myTest35_1 = new MyTest35_1(); myTest35_1.setX(8); in = 20; } public void setX(int x) { this.x = x; } }
测试:
@Test public void test() { ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring_1_100/config_31_40/spring35_resource_inject.xml"); Boss boss = (Boss) ac.getBean("boss"); System.out.println(boss.getCar()); }
执行javac -verbose
Classfile /Users/quyixiao/project/spring_tiny/target/classes/com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.class Last modified 2020-10-11; size 911 bytes MD5 checksum 4a1f4eae2b03a3a38505911fd4ffa3e4 Compiled from "MyTest35_1.java" public class com.spring_1_100.test_31_40.test35_resource_inject.MyTest35_1 minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #10.#34 // java/lang/Object."":()V #2 = String #35 // Welcome #3 = Fieldref #5.#36 // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.str:Ljava/lang/String; #4 = Fieldref #5.#37 // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.x:I #5 = Class #38 // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1 #6 = Methodref #5.#34 // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1."":()V #7 = Methodref #5.#39 // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.setX:(I)V #8 = Methodref #40.#41 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; #9 = Fieldref #5.#42 // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.in:Ljava/lang/Integer; #10 = Class #43 // java/lang/Object #11 = Utf8 str #12 = Utf8 Ljava/lang/String; #13 = Utf8 x #14 = Utf8 I #15 = Utf8 in #16 = Utf8 Ljava/lang/Integer; #17 = Utf8 #18 = Utf8 ()V #19 = Utf8 Code #20 = Utf8 LineNumberTable #21 = Utf8 LocalVariableTable #22 = Utf8 this #23 = Utf8 Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1; #24 = Utf8 main #25 = Utf8 ([Ljava/lang/String;)V #26 = Utf8 args #27 = Utf8 [Ljava/lang/String; #28 = Utf8 myTest35_1 #29 = Utf8 setX #30 = Utf8 (I)V #31 = Utf8 #32 = Utf8 SourceFile #33 = Utf8 MyTest35_1.java #34 = NameAndType #17:#18 // "":()V #35 = Utf8 Welcome #36 = NameAndType #11:#12 // str:Ljava/lang/String; #37 = NameAndType #13:#14 // x:I #38 = Utf8 com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1 #39 = NameAndType #29:#30 // setX:(I)V #40 = Class #44 // java/lang/Integer #41 = NameAndType #45:#46 // valueOf:(I)Ljava/lang/Integer; #42 = NameAndType #15:#16 // in:Ljava/lang/Integer; #43 = Utf8 java/lang/Object #44 = Utf8 java/lang/Integer #45 = Utf8 valueOf #46 = Utf8 (I)Ljava/lang/Integer; { java.lang.String str; descriptor: Ljava/lang/String; flags: public com.spring_1_100.test_31_40.test35_resource_inject.MyTest35_1(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: aload_0 5: ldc #2 // String Welcome 7: putfield #3 // Field str:Ljava/lang/String; 10: aload_0 11: iconst_5 12: putfield #4 // Field x:I 15: return LineNumberTable: line 3: 0 line 4: 4 line 5: 10 LocalVariableTable: Start Length Slot Name Signature 0 16 0 this Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=1 0: new #5 // class com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1 3: dup 4: invokespecial #6 // Method "":()V 7: astore_1 8: aload_1 9: bipush 8 11: invokevirtual #7 // Method setX:(I)V 14: bipush 20 16: invokestatic #8 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 19: putstatic #9 // Field in:Ljava/lang/Integer; 22: return LineNumberTable: line 9: 0 line 10: 8 line 11: 14 line 12: 22 LocalVariableTable: Start Length Slot Name Signature 0 23 0 args [Ljava/lang/String; 8 15 1 myTest35_1 Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1; public void setX(int); descriptor: (I)V flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=2 0: aload_0 1: iload_1 2: putfield #4 // Field x:I 5: return LineNumberTable: line 15: 0 line 16: 5 LocalVariableTable: Start Length Slot Name Signature 0 6 0 this Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1; 0 6 1 x I static {}; descriptor: ()V flags: ACC_STATIC Code: stack=1, locals=0, args_size=0 0: bipush 10 2: invokestatic #8 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 5: putstatic #9 // Field in:Ljava/lang/Integer; 8: return LineNumberTable: line 6: 0 } SourceFile: "MyTest35_1.java"
在学习之前,还是和多前一样,来了解一下java类字节码结构 。
类字节码文件完整结构:
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
Class 文件结构中常量池中11种数据类型的结构总表
Class 文件结构中常量池中11种数据类型的结构总表 | |||
---|---|---|---|
常量 | 项目 | 类型 | 描述 |
CONSTANT_Utf8_info | tag | U1 | 值为1 |
length | U2 | UTF-8编码字符串长度 | |
bytes | U(length) | 长度为 length 的 UTF-8编码的字符串 | |
CONSTANT_Integer_info | tag | U1 | 值为3 |
bytes | U4 | 按照高位在前存储 int 值 | |
CONSTANT_Float_info | tag | U1 | 值为4 |
bytes | U4 | 按照高位在前存储 float 值 | |
CONSTANT_Long_info | tag | U1 | 值为5 |
bytes | U8 | 按照高位在前存储 long 值 | |
CONSTANT_Double_info | tag | U1 | 值为6 |
bytes | U8 | 按照高位在前存储 double 值 | |
CONSTANT_Class_info | tag | U1 | 值为7 |
index | U2 | 指向全限定名常量项的索引 | |
CONSTANT_String_info | tag | U1 | 值为8 |
index | U2 | 指向字符串字面量索引 | |
CONSTANT_FieIdref_info | tag | U1 | 值为9 |
index | U2 | 指向声明字段的类或者接口描述符CONSTANT_Class_info的索引项 | |
index | U2 | 指向字段描述符 CONSTANT_NameAndType_Info 的索引项 | |
CONSTANT_Methodref_info | tag | U1 | 值为10 |
index | U2 | 指向声明方法的类描述符CONSTANT_Class_info的索引项 | |
index | U2 | 指向名称及类型描述符 CONSTANT_NameAndType_Info 的索引项 | |
CONSTANT_InterfaceMethodref_info | tag | U1 | 值为11 |
index | U2 | 指向声明方法的类描述符CONSTANT_Class_info的索引项 | |
index | U2 | 指向名称及类型描述符 CONSTANT_NameAndType_Info 的索引项 | |
CONSTANT_NameAndType_info | tag | U1 | 值为12 |
index | U2 | 指向该字段或方法名称常量项索引 | |
index | U2 | 指向该字段或方法描述符常量项的索引 |
标志名称 | 标志值 | 含义 | 修饰对象 |
---|---|---|---|
ACC_PUBLIC | 0x0001 | 是否为Public类型 | class, field, method |
ACC_PRIVATE | 0x0002 | 是否为private类型 | class, field, method |
ACC_PROTECTED | 0x0004 | 是否为protected类型 | class, field, method |
ACC_STATIC | 0x0008 | static 类型 | field, method |
ACC_FINAL | 0x0010 | 是否被声明为final,只有类可以设置 | class, field, method, parameter |
ACC_SUPER | 0x0020 | 是否允许使用invokespecial字节码指令的新语义. | class |
ACC_SYNCHRONIZED | 0x0020 | synchronized类型 | method |
ACC_VOLATILE | 0x0040 | volatile类型 | field |
ACC_BRIDGE | 0x0040 | bridge类型 | method |
ACC_VARARGS | 0x0080 | varargs类型 | method |
ACC_TRANSIENT | 0x0080 | transient类型 | field |
ACC_NATIVE | 0x0100 | native 类型 | method |
ACC_INTERFACE | 0x0200 | 标志这是一个接口 | class |
ACC_ABSTRACT | 0x0400 | 是否为abstract类型,对于接口或者抽象类来说, 次标志值为真,其他类型为假 | class, method |
ACC_STRICT | 0x0800 | strict类型 | method |
ACC_SYNTHETIC | 0x1000 | 标志这个类并非由用户代码产生 | class, field, method, parameter |
ACC_ANNOTATION | 0x2000 | 标志这是一个注解 | class |
ACC_ENUM | 0x4000 | 标志这是一个枚举 | class(?) field inner |
ACC_MANDATED | 0x8000 | mandated类型 | parameter |
-
属性表结构
field_info{
u2 access_flags; 0002
u2 name_index; 0006
u2 descriptor_index; 0006
u2 attributes_count; 0000
attribute_info attributes[attributes_count];
} -
方法表结构
method_info{
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
} -
方法的属性结构,方法中每个属性都是一个 attribute_info 结构 ,JVM预定义了部分 attribute,但是编译器自己也可以实现了自己的 attribute 写入 class 文件里,供运行时使用。不同的 attribute 通过attribute_name_index 来区分。
attribute_info{
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
} -
Code结构:Code Attribute 的作用是保存该方法的结构,如所对应的字节码
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{
u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_lenght];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
- attribute_length:表示 attribute所包含的字节数,不包含 attribute_name_index 和attribute_length 字段 。
- max_stack:表示这个方法运行的任何时刻所能达到的操作数栈的最大深度。
- max_locals:表示方法执行期间创建的局部变量的数目,包含用来表示传入的参数的局部变量。
- code_length:表示该方法所包含的字节码的字节数以及具体的指令码。具体字节码即是该方法被调用时,虚拟机所执行的字节码。
- exception_table:这里存放的是处理异常的信息。每个exception_table 表项由start_pc,end_pc,handler_pc,catch_type 组成
- start_pc 和 end_pc :表示在 code数组中的从 start_pc 到 end_pc (包含 start_pc,不包含 end_pc) 的指令抛出的异常会由这个表项来处理。
- handler_pc:表示处理异常的代码的开始处。 catch_type:表示会被处理的异常类型,它指向常量池里的一个异常类,当 catch_type 为0时,表示处理所有的异常。
-
附加属性
LineNumberTable:这个属性用来表示 code 数组中的字节码和 Java 代码行数之间的关系,这个属性可以用来在调试的时候定位代码执行的行数。
LineNumberTable 的结构录下:
LineNumberTable_attribute{
u2 attribute_name_index;
u4 attribute_length;
u2 line_number_table_length;
{
u2 start_pc;
u2 line_number;
}
} -
助记符
- aload_0 = 42 (0x2a) : 将索引为0的元素推送到操作栈的栈顶。
- invokespecial :调用实例方法,后面可以接参数。
- iconst_<i>: 定义常量。
- putfield:将栈顶的元素赋值。
- return : 表示返回。
了解了上面的关于字节码的基本知识以后,下面,我们来开启我们的Spring源码之-ASM解析
SimpleMetadataReader.java
SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException { //将resource资源转化成inputstream InputStream is = new BufferedInputStream(resource.getInputStream()); ClassReader classReader; try { //初始化常量池数据 classReader = new ClassReader(is); } catch (IllegalArgumentException ex) { throw new NestedIOException("ASM ClassReader failed to parse class file - " + "probably due to a new Java class file version that isn't supported yet: " + resource, ex); } finally { is.close(); } AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader); //访问魔数,jdk版本号,类的访问标志位,当前类,父类,接口数,属性,方法,以及属性信息 classReader.accept(visitor, ClassReader.SKIP_DEBUG); this.annotationMetadata = visitor; this.classMetadata = visitor; this.resource = resource; }
ClassReader.java
public ClassReader(final InputStream is) throws IOException { //通过readClass方法,将InputStream转化成字节码文件 this(readClass(is, false)); }
[-54, -2, -70, -66, 0, 0, 0, 51, 0, 47, 10, 0, 10, 0, 34, 8, 0, 35, 9, 0, 5, 0, 36, 9, 0, 5, 0, 37, 7, 0, 38, 10, 0, 5, 0, 34, 10, 0, 5, 0, 39, 10, 0, 40, 0, 41, 9, 0, 5, 0, 42, 7, 0, 43, 1, 0, 3, 115, 116, 114, 1, 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 1, 120, 1, 0, 1, 73, 1, 0, 2, 105, 110, 1, 0, 19, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 73, 110, 116, 101, 103, 101, 114, 59, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 18, 76, 111, 99, 97, 108, 86, 97, 114, 105, 97, 98, 108, 101, 84, 97, 98, 108, 101, 1, 0, 4, 116, 104, 105, 115, 1, 0, 63, 76, 99, 111, 109, 47, 115, 112, 114, 105, 110, 103, 95, 49, 95, 49, 48, 48, 47, 116, 101, 115, 116, 95, 51, 49, 95, 52, 48, 47, 116, 101, 115, 116, 51, 53, 95, 114, 101, 115, 111, 117, 114, 99, 101, 95, 105, 110, 106, 101, 99, 116, 47, 77, 121, 84, 101, 115, 116, 51, 53, 95, 49, 59, 1, 0, 4, 109, 97, 105, 110, 1, 0, 22, 40, 91, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86, 1, 0, 4, 97, 114, 103, 115, 1, 0, 19, 91, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 10, 109, 121, 84, 101, 115, 116, 51, 53, 95, 49, 1, 0, 4, 115, 101, 116, 88, 1, 0, 4, 40, 73, 41, 86, 1, 0, 8, 60, 99, 108, 105, 110, 105, 116, 62, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 15, 77, 121, 84, 101, 115, 116, 51, 53, 95, 49, 46, 106, 97, 118, 97, 12, 0, 17, 0, 18, 1, 0, 7, 87, 101, 108, 99, 111, 109, 101, 12, 0, 11, 0, 12, 12, 0, 13, 0, 14, 1, 0, 61, 99, 111, 109, 47, 115, 112, 114, 105, 110, 103, 95, 49, 95, 49, 48, 48, 47, 116, 101, 115, 116, 95, 51, 49, 95, 52, 48, 47, 116, 101, 115, 116, 51, 53, 95, 114, 101, 115, 111, 117, 114, 99, 101, 95, 105, 110, 106, 101, 99, 116, 47, 77, 121, 84, 101, 115, 116, 51, 53, 95, 49, 12, 0, 29, 0, 30, 7, 0, 44, 12, 0, 45, 0, 46, 12, 0, 15, 0, 16, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 1, 0, 17, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 73, 110, 116, 101, 103, 101, 114, 1, 0, 7, 118, 97, 108, 117, 101, 79, 102, 1, 0, 22, 40, 73, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 73, 110, 116, 101, 103, 101, 114, 59, 0, 33, 0, 5, 0, 10, 0, 0, 0, 3, 0, 0, 0, 11, 0, 12, 0, 0, 0, 2, 0, 13, 0, 14, 0, 0, 0, 10, 0, 15, 0, 16, 0, 0, 0, 4, 0, 1, 0, 17, 0, 18, 0, 1, 0, 19, 0, 0, 0, 66, 0, 2, 0, 1, 0, 0, 0, 16, 42, -73, 0, 1, 42, 18, 2, -75, 0, 3, 42, 8, -75, 0, 4, -79, 0, 0, 0, 2, 0, 20, 0, 0, 0, 14, 0, 3, 0, 0, 0, 3, 0, 4, 0, 4, 0, 10, 0, 5, 0, 21, 0, 0, 0, 12, 0, 1, 0, 0, 0, 16, 0, 22, 0, 23, 0, 0, 0, 9, 0, 24, 0, 25, 0, 1, 0, 19, 0, 0, 0, 87, 0, 2, 0, 2, 0, 0, 0, 23, -69, 0, 5, 89, -73, 0, 6, 76, 43, 16, 8, -74, 0, 7, 16, 20, -72, 0, 8, -77, 0, 9, -79, 0, 0, 0, 2, 0, 20, 0, 0, 0, 18, 0, 4, 0, 0, 0, 9, 0, 8, 0, 10, 0, 14, 0, 11, 0, 22, 0, 12, 0, 21, 0, 0, 0, 22, 0, 2, 0, 0, 0, 23, 0, 26, 0, 27, 0, 0, 0, 8, 0, 15, 0, 28, 0, 23, 0, 1, 0, 1, 0, 29, 0, 30, 0, 1, 0, 19, 0, 0, 0, 62, 0, 2, 0, 2, 0, 0, 0, 6, 42, 27, -75, 0, 4, -79, 0, 0, 0, 2, 0, 20, 0, 0, 0, 10, 0, 2, 0, 0, 0, 15, 0, 5, 0, 16, 0, 21, 0, 0, 0, 22, 0, 2, 0, 0, 0, 6, 0, 22, 0, 23, 0, 0, 0, 0, 0, 6, 0, 13, 0, 14, 0, 1, 0, 8, 0, 31, 0, 18, 0, 1, 0, 19, 0, 0, 0, 33, 0, 1, 0, 0, 0, 0, 0, 9, 16, 10, -72, 0, 8, -77, 0, 9, -79, 0, 0, 0, 1, 0, 20, 0, 0, 0, 6, 0, 1, 0, 0, 0, 6, 0, 1, 0, 32, 0, 0, 0, 2, 0, 33]
public ClassReader(final byte[] b, final int off, final int len) { this.b = b; // checks the class version /* SPRING PATCH: REMOVED FOR FORWARD COMPATIBILITY WITH JDK 9 if (readShort(off + 6) > Opcodes.V1_8) { throw new IllegalArgumentException(); } */ // parses the constant pool 我们根据类的类字节码文件完整结构知道,前面4个字节表示魔数,接着4个字节表示次版本号,主版本号, 因此批索引为8(包含8)向后数两个字节表示常量池的个数(数组下标从0开始)。 readUnsignedShort()这个方法主要是计算索引及索引的下一位,2个字节的16进制数转化为10进制数,比如01 01 转化为10进制数为 257, 在这里,我们得到当前类有47个常量,而items主要记录常量池中每个常量在字节码中的起始索引 items = new int[readUnsignedShort(off + 8)]; //常量池中的常量个数 int n = items.length; strings = new String[n]; int max = 0; int index = off + 10; for (int i = 1; i < n; ++i) { items[i] = index + 1; int size; switch (b[index]) { //根据Class 文件结构中常量池中11种数据类型的结构总表中,得到属性,方法,int,float,CONSTANT_NameAndType_info等,这些类型都是占5个字节 case ClassWriter.FIELD: case ClassWriter.METH: case ClassWriter.IMETH: case ClassWriter.INT: case ClassWriter.FLOAT: case ClassWriter.NAME_TYPE: case ClassWriter.INDY: size = 5; break; //对于Long类型,Double类型,占9个字节 case ClassWriter.LONG: case ClassWriter.DOUBLE: size = 9; ++i; break; case ClassWriter.UTF8: //对于UTF8类型,第一个字节01表示当前常量是CONSTANT_Utf8_info, 而接着后面两个字节是UTF8常量的所占字节数,因此,UTF8常量所占总字节数=1 + 2 + 字符串所占字节个数 size = 3 + readUnsignedShort(index + 1); if (size > max) { max = size; } break; case ClassWriter.HANDLE: size = 4; break; // case ClassWriter.CLASS: // case ClassWriter.STR: // case ClassWriter.MTYPE 如果是常量CONSTANT_Class_info,CONSTANT_String_info都占三个字节 default: size = 3; break; } index += size; } //UTF8的最大字符串的长度 maxStringLength = max; //header记录常量池解析结束后所在字节码中的索引 header = index; }
到这里常量池终于记录完了,生成了一个items数组,数组记录每个常量池所在字节码处的下标,并用header记录常量池结束所在字节码所在的索引 。
ClassReader.java
public void accept(final ClassVisitor classVisitor, final int flags) { accept(classVisitor, new Attribute[0], flags); }
ClassReader.java
public void accept(final ClassVisitor classVisitor, final Attribute[] attrs, final int flags) { int u = header; //常量池之后的第一个字节码索引 char[] c = new char[maxStringLength]; //读UTF8常量的缓存变量,maxStringLength : UTF8常量的最大长度 Context context = new Context(); context.attrs = attrs; context.flags = flags; context.buffer = c; //根据类的 类字节码文件完整结构 常量池之后的两个字节表示类的访问标志位,这里得到的是0x0021,根据访问标志位表, 我们知道0x0021由0x0020和0x0001组成,分别对应了ACC_PUBLIC,ACC_SUPER int access = readUnsignedShort(u); //根据 类字节码文件完整结构 知道访问标志位的后两位是当前类名 String name = readClass(u + 2, c); //当前类名后两位是父类名称,这里是java.lang.Object String superClass = readClass(u + 4, c); //父类名称的后两位是接口数 String[] interfaces = new String[readUnsignedShort(u + 6)]; //字节码向后移动8位,移动到当前类所有接口的字节码所在索引 u += 8; for (int i = 0; i < interfaces.length; ++i) { //每个接口名称索引占两个字节,并且指向常量池 interfaces[i] = readClass(u, c); u += 2; } //读取class类的所有属性 String signature = null; String sourceFile = null; String sourceDebug = null; String enclosingOwner = null; String enclosingName = null; String enclosingDesc = null; int anns = 0; int ianns = 0; int tanns = 0; int itanns = 0; int innerClasses = 0; Attribute attributes = null; //这个方法主要是跳过类的所有的属性和方法 u = getAttributes(); for (int i = readUnsignedShort(u); i > 0; --i) { String attrName = readUTF8(u + 2, c); //如果属性名是源文件 if ("SourceFile".equals(attrName)) { //如果属性名是源文件,则向后加8个字节,得到文件名,如图 源文件字节码结构,向后读取8个字节,得到源文件的名字 sourceFile = readUTF8(u + 8, c); } else if ("InnerClasses".equals(attrName)) {//如果是内部类 innerClasses = u + 8; } else if ("EnclosingMethod".equals(attrName)) { enclosingOwner = readClass(u + 8, c); int item = readUnsignedShort(u + 10); if (item != 0) { enclosingName = readUTF8(items[item], c); enclosingDesc = readUTF8(items[item] + 2, c); } } else if (SIGNATURES && "Signature".equals(attrName)) { signature = readUTF8(u + 8, c); } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) {//如果包含注解 anns = u + 8; } else if (ANNOTATIONS && "RuntimeVisibleTypeAnnotations".equals(attrName)) { tanns = u + 8; } else if ("Deprecated".equals(attrName)) { access |= Opcodes.ACC_DEPRECATED; } else if ("Synthetic".equals(attrName)) { access |= Opcodes.ACC_SYNTHETIC | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; } else if ("SourceDebugExtension".equals(attrName)) { int len = readInt(u + 4); sourceDebug = readUTF(u + 8, len, new char[len]); } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) { ianns = u + 8; } else if (ANNOTATIONS && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { itanns = u + 8; } else if ("BootstrapMethods".equals(attrName)) { int[] bootstrapMethods = new int[readUnsignedShort(u + 8)]; for (int j = 0, v = u + 10; j < bootstrapMethods.length; j++) { bootstrapMethods[j] = v; v += 2 + readUnsignedShort(v + 2) << 1; } context.bootstrapMethods = bootstrapMethods; } else { Attribute attr = readAttribute(attrs, attrName, u + 8, readInt(u + 4), c, -1, null); if (attr != null) { attr.next = attributes; attributes = attr; } } u += 6 + readInt(u + 4); } //readInt(items[1] - 7) 主要得到jdk的版本号,为什么是-7, 因为item[1]本身占一位,常量池数占2位,次版本号占2位,主版本号占2位,总共7位,两路后读取4个字节,就是jdk版本号 access:访问标志位 name:类全名称 superClass:父类 interfaces:类实现所有的接口 classVisitor.visit(readInt(items[1] - 7), access, name, signature, superClass, interfaces); // visits the source and debug info if ((flags & SKIP_DEBUG) == 0 && (sourceFile != null || sourceDebug != null)) { classVisitor.visitSource(sourceFile, sourceDebug); } // visits the outer class if (enclosingOwner != null) { classVisitor.visitOuterClass(enclosingOwner, enclosingName, enclosingDesc); } //如果类中包含注解,则访问注解中的值,保存到map中 if (ANNOTATIONS && anns != 0) { for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { v = readAnnotationValues(v + 2, c, true, classVisitor.visitAnnotation(readUTF8(v, c), true)); } } if (ANNOTATIONS && ianns != 0) { for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { v = readAnnotationValues(v + 2, c, true, classVisitor.visitAnnotation(readUTF8(v, c), false)); } } if (ANNOTATIONS && tanns != 0) { for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { v = readAnnotationTarget(context, v); v = readAnnotationValues(v + 2, c, true, classVisitor.visitTypeAnnotation(context.typeRef, context.typePath, readUTF8(v, c), true)); } } if (ANNOTATIONS && itanns != 0) { for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { v = readAnnotationTarget(context, v); v = readAnnotationValues(v + 2, c, true, classVisitor.visitTypeAnnotation(context.typeRef, context.typePath, readUTF8(v, c), false)); } } //如果类中包含atributes ,则访问attribute while (attributes != null) { Attribute attr = attributes.next; attributes.next = null; classVisitor.visitAttribute(attributes); attributes = attr; } //如果类包含内部类 if (innerClasses != 0) { int v = innerClasses + 2; for (int i = readUnsignedShort(innerClasses); i > 0; --i) { classVisitor.visitInnerClass(readClass(v, c), readClass(v + 2, c), readUTF8(v + 4, c), readUnsignedShort(v + 6)); v += 8; } } u = header + 10 + 2 * interfaces.length; for (int i = readUnsignedShort(u - 2); i > 0; --i) { //访问类中的所有的属性 u = readField(classVisitor, context, u); } u += 2; for (int i = readUnsignedShort(u - 2); i > 0; --i) { //访问类中的所有方法 u = readMethod(classVisitor, context, u); } //访问结束 classVisitor.visitEnd(); }
ClassReader.java
private int getAttributes() { //跳过访问标志位(2) ,当前类(2),父类(2),接口数(2) ,接口(2 * 接口数) int u = header + 8 + readUnsignedShort(header + 6) * 2; //跳过所有的属性 for (int i = readUnsignedShort(u); i > 0; --i) { for (int j = readUnsignedShort(u + 8); j > 0; --j) { u += 6 + readInt(u + 12); } //根据属性表结构,每一个属性占8个字节 u += 8; } u += 2; //跳过所有的方法 for (int i = readUnsignedShort(u); i > 0; --i) { for (int j = readUnsignedShort(u + 8); j > 0; --j) { u += 6 + readInt(u + 12); } u += 8; } // return u + 2; }
上面这一段代码确实让人费解,一下又是+6,一下又是+8的,这是什么意思呢?
此时,读取到方法的个数是4个。而在u+8,向后移动8个字节,再读取两个字节,表示方法属性个数,因此j = 1
而u + 12 如下图所示,表示方法属性所占的字节数。
这个方法执行完成以后,己经跳过了类的所有的属性和方法。
ClassMetadataReadingVisitor.java
public void visit(int version, int access, String name, String signature, String supername, String[] interfaces) { this.className = ClassUtils.convertResourcePathToClassName(name); //当前类的命名,将 "/" 替换成 "." //是否是接口,当前类的访问标志是0x0021,而ACC_INTERFACE 是0x0200,因此0x0021 & 0x0200 不是接口,下面其他的同理 this.isInterface = ((access & Opcodes.ACC_INTERFACE) != 0); this.isAnnotation = ((access & Opcodes.ACC_ANNOTATION) != 0); this.isAbstract = ((access & Opcodes.ACC_ABSTRACT) != 0); this.isFinal = ((access & Opcodes.ACC_FINAL) != 0); if (supername != null && !this.isInterface) { //如果不是接口,获取父类名 this.superClassName = ClassUtils.convertResourcePathToClassName(supername); } this.interfaces = new String[interfaces.length]; for (int i = 0; i < interfaces.length; i++) { //保存当前类实现的所有的接口 this.interfaces[i] = ClassUtils.convertResourcePathToClassName(interfaces[i]); } }
ClassReader.java
private int readField(final ClassVisitor classVisitor, final Context context, int u) { char[] c = context.buffer; //当前属性的访问标识符,public,protected,default等 int access = readUnsignedShort(u); //当前属性名称 String name = readUTF8(u + 2, c); //当前属性的描述,比如,属性是int类型,还是Integer类型,Ljava/lang/String; String desc = readUTF8(u + 4, c); u += 6; String signature = null; int anns = 0; int ianns = 0; int tanns = 0; int itanns = 0; Object value = null; Attribute attributes = null; for (int i = readUnsignedShort(u); i > 0; --i) { String attrName = readUTF8(u + 2, c); if ("ConstantValue".equals(attrName)) { int item = readUnsignedShort(u + 8); value = item == 0 ? null : readConst(item, c); } else if (SIGNATURES && "Signature".equals(attrName)) { signature = readUTF8(u + 8, c); } else if ("Deprecated".equals(attrName)) { access |= Opcodes.ACC_DEPRECATED; } else if ("Synthetic".equals(attrName)) { access |= Opcodes.ACC_SYNTHETIC | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) { anns = u + 8; } else if (ANNOTATIONS && "RuntimeVisibleTypeAnnotations".equals(attrName)) { tanns = u + 8; } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) { ianns = u + 8; } else if (ANNOTATIONS && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { itanns = u + 8; } else { Attribute attr = readAttribute(context.attrs, attrName, u + 8, readInt(u + 4), c, -1, null); if (attr != null) { attr.next = attributes; attributes = attr; } } u += 6 + readInt(u + 4); } u += 2; //创建一个空的EmptyFieldVisitor对象 FieldVisitor fv = classVisitor.visitField(access, name, desc, signature, value); if (fv == null) { return u; } //访问属性中的所有的注解,这一点,在分析Spring源码,关于注解这一块,将做重点分析 if (ANNOTATIONS && anns != 0) { for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { v = readAnnotationValues(v + 2, c, true, fv.visitAnnotation(readUTF8(v, c), true)); } } if (ANNOTATIONS && ianns != 0) { for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { v = readAnnotationValues(v + 2, c, true, fv.visitAnnotation(readUTF8(v, c), false)); } } if (ANNOTATIONS && tanns != 0) { for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { v = readAnnotationTarget(context, v); v = readAnnotationValues(v + 2, c, true, fv.visitTypeAnnotation(context.typeRef, context.typePath, readUTF8(v, c), true)); } } if (ANNOTATIONS && itanns != 0) { for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { v = readAnnotationTarget(context, v); v = readAnnotationValues(v + 2, c, true, fv.visitTypeAnnotation(context.typeRef, context.typePath, readUTF8(v, c), false)); } } //访问属性attributes,关于attributes是什么,一般一些编译器会在编译时向类字节码中加入一些编译器相关的属性,方便编译器对类做相关的操作 while (attributes != null) { Attribute attr = attributes.next; attributes.next = null; fv.visitAttribute(attributes); attributes = attr; } //属性访问结束相关操作 fv.visitEnd(); return u; }
ClassReader.java
private int readMethod(final ClassVisitor classVisitor, final Context context, int u) { char[] c = context.buffer; //当前方法访问标识符 context.access = readUnsignedShort(u); //当前方法名称 context.name = readUTF8(u + 2, c); //当前方法描述 ([Ljava/lang/String;)V ,表示传入String类型,方法返回值为void context.desc = readUTF8(u + 4, c); u += 6; int code = 0; int exception = 0; String[] exceptions = null; String signature = null; int methodParameters = 0; int anns = 0; int ianns = 0; int tanns = 0; int itanns = 0; int dann = 0; int mpanns = 0; int impanns = 0; int firstAttribute = u; Attribute attributes = null; for (int i = readUnsignedShort(u); i > 0; --i) { String attrName = readUTF8(u + 2, c); //当前方法结构 if ("Code".equals(attrName)) { //如果是code结构,索引向后移动8个字节 if ((context.flags & SKIP_CODE) == 0) { code = u + 8; } } else if ("Exceptions".equals(attrName)) { exceptions = new String[readUnsignedShort(u + 8)]; exception = u + 10; for (int j = 0; j < exceptions.length; ++j) { exceptions[j] = readClass(exception, c); exception += 2; } } else if (SIGNATURES && "Signature".equals(attrName)) { signature = readUTF8(u + 8, c); } else if ("Deprecated".equals(attrName)) { context.access |= Opcodes.ACC_DEPRECATED; } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) { anns = u + 8; } else if (ANNOTATIONS && "RuntimeVisibleTypeAnnotations".equals(attrName)) { tanns = u + 8; } else if (ANNOTATIONS && "AnnotationDefault".equals(attrName)) { dann = u + 8; } else if ("Synthetic".equals(attrName)) { context.access |= Opcodes.ACC_SYNTHETIC | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) { ianns = u + 8; } else if (ANNOTATIONS && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { itanns = u + 8; } else if (ANNOTATIONS && "RuntimeVisibleParameterAnnotations".equals(attrName)) { mpanns = u + 8; } else if (ANNOTATIONS && "RuntimeInvisibleParameterAnnotations".equals(attrName)) { impanns = u + 8; } else if ("MethodParameters".equals(attrName)) { methodParameters = u + 8; } else { Attribute attr = readAttribute(context.attrs, attrName, u + 8, readInt(u + 4), c, -1, null); if (attr != null) { attr.next = attributes; attributes = attr; } } //跳过Code结构所占的字节 u += 6 + readInt(u + 4); } u += 2; //访问所有的方法 MethodVisitor mv = classVisitor.visitMethod(context.access, context.name, context.desc, signature, exceptions); if (mv == null) { return u; } /** 如果返回的MethodVisitor实际上是MethodWriter,则表示在读取器和编写器之间没有方法适配器。如果,在 * 此外,写入程序的常量池是从该读取器复制的(兆瓦cw.cr==this),以及方法的签名和异常未更改,则可以跳过所有访问事件 * 只需将方法的原始代码复制到编写器(访问、名称和描述符可以更改,但不是这样很重要,因为它们不是从读者那里复制的)。 */ if (WRITER && mv instanceof MethodWriter) { MethodWriter mw = (MethodWriter) mv; if (mw.cw.cr == this && (signature != null ? signature.equals(mw.signature) : mw.signature == null)) { boolean sameExceptions = false; if (exceptions == null) { sameExceptions = mw.exceptionCount == 0; } else if (exceptions.length == mw.exceptionCount) { sameExceptions = true; for (int j = exceptions.length - 1; j >= 0; --j) { exception -= 2; if (mw.exceptions[j] != readUnsignedShort(exception)) { sameExceptions = false; break; } } } if (sameExceptions) { /* * we do not copy directly the code into MethodWriter to * save a byte array copy operation. The real copy will be * done in ClassWriter.toByteArray(). */ mw.classReaderOffset = firstAttribute; mw.classReaderLength = u - firstAttribute; return u; } } } //访问方法的所有的参数 if (methodParameters != 0) { for (int i = b[methodParameters] & 0xFF, v = methodParameters + 1; i > 0; --i, v = v + 4) { mv.visitParameter(readUTF8(v, c), readUnsignedShort(v + 2)); } } //访问方法的所有的注解 if (ANNOTATIONS && dann != 0) { AnnotationVisitor dv = mv.visitAnnotationDefault(); readAnnotationValue(dann, c, null, dv); if (dv != null) { dv.visitEnd(); } } if (ANNOTATIONS && anns != 0) { for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { v = readAnnotationValues(v + 2, c, true, mv.visitAnnotation(readUTF8(v, c), true)); } } if (ANNOTATIONS && ianns != 0) { for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { v = readAnnotationValues(v + 2, c, true, mv.visitAnnotation(readUTF8(v, c), false)); } } if (ANNOTATIONS && tanns != 0) { for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { v = readAnnotationTarget(context, v); v = readAnnotationValues(v + 2, c, true, mv.visitTypeAnnotation(context.typeRef, context.typePath, readUTF8(v, c), true)); } } if (ANNOTATIONS && itanns != 0) { for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { v = readAnnotationTarget(context, v); v = readAnnotationValues(v + 2, c, true, mv.visitTypeAnnotation(context.typeRef, context.typePath, readUTF8(v, c), false)); } } if (ANNOTATIONS && mpanns != 0) { readParameterAnnotations(mv, context, mpanns, true); } if (ANNOTATIONS && impanns != 0) { readParameterAnnotations(mv, context, impanns, false); } //访问方法的所有的属性 while (attributes != null) { Attribute attr = attributes.next; attributes.next = null; mv.visitAttribute(attributes); attributes = attr; } //访问方法的所有的Code if (code != 0) { mv.visitCode(); readCode(mv, context, code); } //方法访问结束处理 mv.visitEnd(); return u; }
ClassReader.java
private void readCode(final MethodVisitor mv, final Context context, int u) { byte[] b = this.b; char[] c = context.buffer; //方法运行的任何时刻所能达到的操作数栈的最大深度 int maxStack = readUnsignedShort(u); //方法执行期间创建的局部变量的数目 int maxLocals = readUnsignedShort(u + 2); //方法所包含的字节码的字节数以及具体的指令码长度 int codeLength = readInt(u + 4); u += 8; //读取字节码发现labels int codeStart = u; int codeEnd = u + codeLength; Label[] labels = context.labels = new Label[codeLength + 2]; readLabel(codeLength + 1, labels); while (u < codeEnd) { int offset = u - codeStart; //得到Code操作指令码 ,下面的循环具体解析如下图 ,指令码相关图 int opcode = b[u] & 0xFF; switch (ClassWriter.TYPE[opcode]) { case ClassWriter.NOARG_INSN: case ClassWriter.IMPLVAR_INSN: u += 1; break; case ClassWriter.LABEL_INSN: readLabel(offset + readShort(u + 1), labels); u += 3; break; case ClassWriter.LABELW_INSN: readLabel(offset + readInt(u + 1), labels); u += 5; break; case ClassWriter.WIDE_INSN: opcode = b[u + 1] & 0xFF; if (opcode == Opcodes.IINC) { u += 6; } else { u += 4; } break; case ClassWriter.TABL_INSN: // skips 0 to 3 padding bytes u = u + 4 - (offset & 3); // reads instruction readLabel(offset + readInt(u), labels); for (int i = readInt(u + 8) - readInt(u + 4) + 1; i > 0; --i) { readLabel(offset + readInt(u + 12), labels); u += 4; } u += 12; break; case ClassWriter.LOOK_INSN: // skips 0 to 3 padding bytes u = u + 4 - (offset & 3); // reads instruction readLabel(offset + readInt(u), labels); for (int i = readInt(u + 4); i > 0; --i) { readLabel(offset + readInt(u + 12), labels); u += 8; } u += 8; break; case ClassWriter.VAR_INSN: case ClassWriter.SBYTE_INSN: case ClassWriter.LDC_INSN: u += 2; break; case ClassWriter.SHORT_INSN: case ClassWriter.LDCW_INSN: case ClassWriter.FIELDORMETH_INSN: case ClassWriter.TYPE_INSN: case ClassWriter.IINC_INSN: u += 3; break; case ClassWriter.ITFMETH_INSN: case ClassWriter.INDYMETH_INSN: u += 5; break; // case MANA_INSN: default: u += 4; break; } } // reads the try catch entries to find the labels, and also visits them for (int i = readUnsignedShort(u); i > 0; --i) { Label start = readLabel(readUnsignedShort(u + 2), labels); Label end = readLabel(readUnsignedShort(u + 4), labels); Label handler = readLabel(readUnsignedShort(u + 6), labels); String type = readUTF8(items[readUnsignedShort(u + 8)], c); mv.visitTryCatchBlock(start, end, handler, type); u += 8; } u += 2; //reads the code attributes int[] tanns = null; //每个可见类型批注的起始索引 int[] itanns = null; //每个不可见类型批注的起始索引 int tann = 0; //tanns数组中的当前索引 int itann = 0;//itanns数组中的当前索引 int ntoff = -1; //下一个可见类型批注代码偏移量 int nitoff = -1; //下一个不可见类型批注代码偏移量 int varTable = 0; int varTypeTable = 0; boolean zip = true; boolean unzip = (context.flags & EXPAND_FRAMES) != 0; int stackMap = 0; int stackMapSize = 0; int frameCount = 0; Context frame = null; Attribute attributes = null; for (int i = readUnsignedShort(u); i > 0; --i) { String attrName = readUTF8(u + 2, c); //局部变量表读取 if ("LocalVariableTable".equals(attrName)) { if ((context.flags & SKIP_DEBUG) == 0) { varTable = u + 8; for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) { int label = readUnsignedShort(v + 10); if (labels[label] == null) { readLabel(label, labels).status |= Label.DEBUG; } label += readUnsignedShort(v + 12); if (labels[label] == null) { readLabel(label, labels).status |= Label.DEBUG; } v += 10; } } } else if ("LocalVariableTypeTable".equals(attrName)) { varTypeTable = u + 8; } else if ("LineNumberTable".equals(attrName)) { //行号表读取 if ((context.flags & SKIP_DEBUG) == 0) { for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) { int label = readUnsignedShort(v + 10); if (labels[label] == null) { readLabel(label, labels).status |= Label.DEBUG; } Label l = labels[label]; while (l.line > 0) { if (l.next == null) { l.next = new Label(); } l = l.next; } l.line = readUnsignedShort(v + 12); v += 4; } } } else if (ANNOTATIONS && "RuntimeVisibleTypeAnnotations".equals(attrName)) { tanns = readTypeAnnotations(mv, context, u + 8, true); ntoff = tanns.length == 0 || readByte(tanns[0]) < 0x43 ? -1 : readUnsignedShort(tanns[0] + 1); } else if (ANNOTATIONS && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { itanns = readTypeAnnotations(mv, context, u + 8, false); nitoff = itanns.length == 0 || readByte(itanns[0]) < 0x43 ? -1 : readUnsignedShort(itanns[0] + 1); } else if (FRAMES && "StackMapTable".equals(attrName)) { if ((context.flags & SKIP_FRAMES) == 0) { stackMap = u + 10; stackMapSize = readInt(u + 4); frameCount = readUnsignedShort(u + 8); } } else if (FRAMES && "StackMap".equals(attrName)) { if ((context.flags & SKIP_FRAMES) == 0) { zip = false; stackMap = u + 10; stackMapSize = readInt(u + 4); frameCount = readUnsignedShort(u + 8); } /* * IMPORTANT! here we assume that the frames are ordered, as in * the StackMapTable attribute, although this is not guaranteed * by the attribute format. */ } else { for (int j = 0; j < context.attrs.length; ++j) { if (context.attrs[j].type.equals(attrName)) { Attribute attr = context.attrs[j].read(this, u + 8, readInt(u + 4), c, codeStart - 8, labels); if (attr != null) { attr.next = attributes; attributes = attr; } } } } u += 6 + readInt(u + 4); } u += 2; // generates the first (implicit) stack map frame if (FRAMES && stackMap != 0) { /* * for the first explicit frame the offset is not offset_delta + 1 * but only offset_delta; setting the implicit frame offset to -1 * allow the use of the "offset_delta + 1" rule in all cases */ frame = context; frame.offset = -1; frame.mode = 0; frame.localCount = 0; frame.localDiff = 0; frame.stackCount = 0; frame.local = new Object[maxLocals]; frame.stack = new Object[maxStack]; if (unzip) { getImplicitFrame(context); } /* * Finds labels for UNINITIALIZED frame types. Instead of decoding * each element of the stack map table, we look for 3 consecutive * bytes that "look like" an UNINITIALIZED type (tag 8, offset * within code bounds, NEW instruction at this offset). We may find * false positives (i.e. not real UNINITIALIZED types), but this * should be rare, and the only consequence will be the creation of * an unneeded label. This is better than creating a label for each * NEW instruction, and faster than fully decoding the whole stack * map table. */ for (int i = stackMap; i < stackMap + stackMapSize - 2; ++i) { if (b[i] == 8) { // UNINITIALIZED FRAME TYPE int v = readUnsignedShort(i + 1); if (v >= 0 && v < codeLength) { if ((b[codeStart + v] & 0xFF) == Opcodes.NEW) { readLabel(v, labels); } } } } } // visits the instructions u = codeStart; while (u < codeEnd) { int offset = u - codeStart; // visits the label and line number for this offset, if any Label l = labels[offset]; if (l != null) { Label next = l.next; l.next = null; mv.visitLabel(l); if ((context.flags & SKIP_DEBUG) == 0 && l.line > 0) { mv.visitLineNumber(l.line, l); while (next != null) { mv.visitLineNumber(next.line, l); next = next.next; } } } // visits the frame for this offset, if any while (FRAMES && frame != null && (frame.offset == offset || frame.offset == -1)) { // if there is a frame for this offset, makes the visitor visit // it, and reads the next frame if there is one. if (frame.offset != -1) { if (!zip || unzip) { mv.visitFrame(Opcodes.F_NEW, frame.localCount, frame.local, frame.stackCount, frame.stack); } else { mv.visitFrame(frame.mode, frame.localDiff, frame.local, frame.stackCount, frame.stack); } } if (frameCount > 0) { stackMap = readFrame(stackMap, zip, unzip, frame); --frameCount; } else { frame = null; } } // visits the instruction at this offset int opcode = b[u] & 0xFF; switch (ClassWriter.TYPE[opcode]) { case ClassWriter.NOARG_INSN: mv.visitInsn(opcode); u += 1; break; case ClassWriter.IMPLVAR_INSN: if (opcode > Opcodes.ISTORE) { opcode -= 59; // ISTORE_0 mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), opcode & 0x3); } else { opcode -= 26; // ILOAD_0 mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3); } u += 1; break; case ClassWriter.LABEL_INSN: mv.visitJumpInsn(opcode, labels[offset + readShort(u + 1)]); u += 3; break; case ClassWriter.LABELW_INSN: mv.visitJumpInsn(opcode - 33, labels[offset + readInt(u + 1)]); u += 5; break; case ClassWriter.WIDE_INSN: opcode = b[u + 1] & 0xFF; if (opcode == Opcodes.IINC) { mv.visitIincInsn(readUnsignedShort(u + 2), readShort(u + 4)); u += 6; } else { mv.visitVarInsn(opcode, readUnsignedShort(u + 2)); u += 4; } break; case ClassWriter.TABL_INSN: { // skips 0 to 3 padding bytes u = u + 4 - (offset & 3); // reads instruction int label = offset + readInt(u); int min = readInt(u + 4); int max = readInt(u + 8); Label[] table = new Label[max - min + 1]; u += 12; for (int i = 0; i < table.length; ++i) { table[i] = labels[offset + readInt(u)]; u += 4; } mv.visitTableSwitchInsn(min, max, labels[label], table); break; } case ClassWriter.LOOK_INSN: { // skips 0 to 3 padding bytes u = u + 4 - (offset & 3); // reads instruction int label = offset + readInt(u); int len = readInt(u + 4); int[] keys = new int[len]; Label[] values = new Label[len]; u += 8; for (int i = 0; i < len; ++i) { keys[i] = readInt(u); values[i] = labels[offset + readInt(u + 4)]; u += 8; } mv.visitLookupSwitchInsn(labels[label], keys, values); break; } case ClassWriter.VAR_INSN: mv.visitVarInsn(opcode, b[u + 1] & 0xFF); u += 2; break; case ClassWriter.SBYTE_INSN: mv.visitIntInsn(opcode, b[u + 1]); u += 2; break; case ClassWriter.SHORT_INSN: mv.visitIntInsn(opcode, readShort(u + 1)); u += 3; break; case ClassWriter.LDC_INSN: mv.visitLdcInsn(readConst(b[u + 1] & 0xFF, c)); u += 2; break; case ClassWriter.LDCW_INSN: mv.visitLdcInsn(readConst(readUnsignedShort(u + 1), c)); u += 3; break; case ClassWriter.FIELDORMETH_INSN: case ClassWriter.ITFMETH_INSN: { int cpIndex = items[readUnsignedShort(u + 1)]; boolean itf = b[cpIndex - 1] == ClassWriter.IMETH; String iowner = readClass(cpIndex, c); cpIndex = items[readUnsignedShort(cpIndex + 2)]; String iname = readUTF8(cpIndex, c); String idesc = readUTF8(cpIndex + 2, c); if (opcode < Opcodes.INVOKEVIRTUAL) { mv.visitFieldInsn(opcode, iowner, iname, idesc); } else { mv.visitMethodInsn(opcode, iowner, iname, idesc, itf); } if (opcode == Opcodes.INVOKEINTERFACE) { u += 5; } else { u += 3; } break; } case ClassWriter.INDYMETH_INSN: { int cpIndex = items[readUnsignedShort(u + 1)]; int bsmIndex = context.bootstrapMethods[readUnsignedShort(cpIndex)]; Handle bsm = (Handle) readConst(readUnsignedShort(bsmIndex), c); int bsmArgCount = readUnsignedShort(bsmIndex + 2); Object[] bsmArgs = new Object[bsmArgCount]; bsmIndex += 4; for (int i = 0; i < bsmArgCount; i++) { bsmArgs[i] = readConst(readUnsignedShort(bsmIndex), c); bsmIndex += 2; } cpIndex = items[readUnsignedShort(cpIndex + 2)]; String iname = readUTF8(cpIndex, c); String idesc = readUTF8(cpIndex + 2, c); mv.visitInvokeDynamicInsn(iname, idesc, bsm, bsmArgs); u += 5; break; } case ClassWriter.TYPE_INSN: mv.visitTypeInsn(opcode, readClass(u + 1, c)); u += 3; break; case ClassWriter.IINC_INSN: mv.visitIincInsn(b[u + 1] & 0xFF, b[u + 2]); u += 3; break; // case MANA_INSN: default: mv.visitMultiANewArrayInsn(readClass(u + 1, c), b[u + 3] & 0xFF); u += 4; break; } // visit the instruction annotations, if any while (tanns != null && tann < tanns.length && ntoff <= offset) { if (ntoff == offset) { int v = readAnnotationTarget(context, tanns[tann]); readAnnotationValues(v + 2, c, true, mv.visitInsnAnnotation(context.typeRef, context.typePath, readUTF8(v, c), true)); } ntoff = ++tann >= tanns.length || readByte(tanns[tann]) < 0x43 ? -1 : readUnsignedShort(tanns[tann] + 1); } while (itanns != null && itann < itanns.length && nitoff <= offset) { if (nitoff == offset) { int v = readAnnotationTarget(context, itanns[itann]); readAnnotationValues(v + 2, c, true, mv.visitInsnAnnotation(context.typeRef, context.typePath, readUTF8(v, c), false)); } nitoff = ++itann >= itanns.length || readByte(itanns[itann]) < 0x43 ? -1 : readUnsignedShort(itanns[itann] + 1); } } if (labels[codeLength] != null) { mv.visitLabel(labels[codeLength]); } // visits the local variable tables if ((context.flags & SKIP_DEBUG) == 0 && varTable != 0) { int[] typeTable = null; if (varTypeTable != 0) { u = varTypeTable + 2; typeTable = new int[readUnsignedShort(varTypeTable) * 3]; for (int i = typeTable.length; i > 0;) { typeTable[--i] = u + 6; // signature typeTable[--i] = readUnsignedShort(u + 8); // index typeTable[--i] = readUnsignedShort(u); // start u += 10; } } u = varTable + 2; for (int i = readUnsignedShort(varTable); i > 0; --i) { int start = readUnsignedShort(u); int length = readUnsignedShort(u + 2); int index = readUnsignedShort(u + 8); String vsignature = null; if (typeTable != null) { for (int j = 0; j < typeTable.length; j += 3) { if (typeTable[j] == start && typeTable[j + 1] == index) { vsignature = readUTF8(typeTable[j + 2], c); break; } } } mv.visitLocalVariable(readUTF8(u + 4, c), readUTF8(u + 6, c), vsignature, labels[start], labels[start + length], index); u += 10; } } // visits the local variables type annotations if (tanns != null) { for (int i = 0; i < tanns.length; ++i) { if ((readByte(tanns[i]) >> 1) == (0x40 >> 1)) { int v = readAnnotationTarget(context, tanns[i]); v = readAnnotationValues(v + 2, c, true, mv.visitLocalVariableAnnotation(context.typeRef, context.typePath, context.start, context.end, context.index, readUTF8(v, c), true)); } } } if (itanns != null) { for (int i = 0; i < itanns.length; ++i) { if ((readByte(itanns[i]) >> 1) == (0x40 >> 1)) { int v = readAnnotationTarget(context, itanns[i]); v = readAnnotationValues(v + 2, c, true, mv.visitLocalVariableAnnotation(context.typeRef, context.typePath, context.start, context.end, context.index, readUTF8(v, c), false)); } } } // visits the code attributes while (attributes != null) { Attribute attr = attributes.next; attributes.next = null; mv.visitAttribute(attributes); attributes = attr; } // visits the max stack and max locals values mv.visitMaxs(maxStack, maxLocals); }
在这个方法中,当打断点调用时,只是将u + n个值,其他的,也没有发现过多的访问。
指令码相关图
最后结束
在本次解析中,我们花费大量的篇幅从代码的角度上来了解字节码的组成结构,事实上我们只得到了类名,和父类名
下面,我们来对本次Spring源码之ASM的解析来做一个总结
- 创建items数组记录所有常量池中每一个常量的起始位置
- 从字节码中获取类的访问标识
- 获取类名
- 获取父类名
- 获取接口数
- 获取所有的接口名称
- 根据访问标识,得到类是不是抽象,是不是final,是不是接口。
- 跳过所有的属性和方法索引,获取源文件名称
- 解析类的所有属性
- 解析类的所有方法
本次解析,只是从代码的角度来了解类字节码的结构,以及是如何解析的。方便以后关于Spring是如何通过ASM 来获取类,方法,以及属性的注解。从而来实现bean的定义的创建。