Spring源码深度解析(郝佳)-学习-ASM 类字节码解析

我们在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_infotagU1值为1
lengthU2UTF-8编码字符串长度
bytesU(length)长度为 length 的 UTF-8编码的字符串
CONSTANT_Integer_infotagU1值为3
bytesU4按照高位在前存储 int 值
CONSTANT_Float_infotagU1值为4
bytesU4按照高位在前存储 float 值
CONSTANT_Long_infotagU1值为5
bytesU8按照高位在前存储 long 值
CONSTANT_Double_infotagU1值为6
bytesU8按照高位在前存储 double 值
CONSTANT_Class_infotagU1值为7
indexU2指向全限定名常量项的索引
CONSTANT_String_infotagU1值为8
indexU2指向字符串字面量索引
CONSTANT_FieIdref_infotagU1值为9
indexU2指向声明字段的类或者接口描述符CONSTANT_Class_info的索引项
indexU2指向字段描述符 CONSTANT_NameAndType_Info 的索引项
CONSTANT_Methodref_infotagU1值为10
indexU2指向声明方法的类描述符CONSTANT_Class_info的索引项
indexU2指向名称及类型描述符 CONSTANT_NameAndType_Info 的索引项
CONSTANT_InterfaceMethodref_infotagU1值为11
indexU2指向声明方法的类描述符CONSTANT_Class_info的索引项
indexU2指向名称及类型描述符 CONSTANT_NameAndType_Info 的索引项
CONSTANT_NameAndType_infotagU1值为12
indexU2指向该字段或方法名称常量项索引
indexU2指向该字段或方法描述符常量项的索引

字节码访问标识位表

标志名称标志值含义修饰对象
ACC_PUBLIC0x0001是否为Public类型class, field, method
ACC_PRIVATE0x0002是否为private类型class, field, method
ACC_PROTECTED0x0004是否为protected类型class, field, method
ACC_STATIC0x0008static 类型field, method
ACC_FINAL0x0010是否被声明为final,只有类可以设置class, field, method, parameter
ACC_SUPER0x0020是否允许使用invokespecial字节码指令的新语义.class
ACC_SYNCHRONIZED0x0020synchronized类型method
ACC_VOLATILE0x0040volatile类型field
ACC_BRIDGE0x0040bridge类型method
ACC_VARARGS0x0080varargs类型method
ACC_TRANSIENT0x0080transient类型field
ACC_NATIVE0x0100native 类型method
ACC_INTERFACE0x0200标志这是一个接口class
ACC_ABSTRACT0x0400是否为abstract类型,对于接口或者抽象类来说, 次标志值为真,其他类型为假class, method
ACC_STRICT0x0800strict类型method
ACC_SYNTHETIC0x1000标志这个类并非由用户代码产生class, field, method, parameter
ACC_ANNOTATION0x2000标志这是一个注解class
ACC_ENUM0x4000标志这是一个枚举class(?) field inner
ACC_MANDATED0x8000mandated类型parameter
  1. 属性表结构
           field_info{
                   u2 access_flags; 0002
                   u2 name_index; 0006
                   u2 descriptor_index; 0006
                   u2 attributes_count; 0000
                   attribute_info attributes[attributes_count];
           }

  2. 方法表结构
    method_info{
            u2         access_flags;
            u2         name_index;
            u2         descriptor_index;
            u2         attributes_count;
            attribute_info         attributes[attributes_count];
    }

  3. 方法的属性结构,方法中每个属性都是一个 attribute_info 结构 ,JVM预定义了部分 attribute,但是编译器自己也可以实现了自己的 attribute 写入 class 文件里,供运行时使用。不同的 attribute 通过attribute_name_index 来区分。
    attribute_info{
            u2        attribute_name_index;
            u4        attribute_length;
            u1        info[attribute_length];
    }

  4. 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时,表示处理所有的异常。
  1. 附加属性
            LineNumberTable:这个属性用来表示 code 数组中的字节码和 Java 代码行数之间的关系,这个属性可以用来在调试的时候定位代码执行的行数。
    LineNumberTable 的结构录下:
            LineNumberTable_attribute{
                    u2        attribute_name_index;
                    u4        attribute_length;
                    u2        line_number_table_length;
                    {
                            u2        start_pc;
                            u2        line_number;
                    }
            }

  2. 助记符

  • 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的解析来做一个总结

  1. 创建items数组记录所有常量池中每一个常量的起始位置
  2. 从字节码中获取类的访问标识
  3. 获取类名
  4. 获取父类名
  5. 获取接口数
  6. 获取所有的接口名称
  7. 根据访问标识,得到类是不是抽象,是不是final,是不是接口。
  8. 跳过所有的属性和方法索引,获取源文件名称
  9. 解析类的所有属性
  10. 解析类的所有方法

本次解析,只是从代码的角度来了解类字节码的结构,以及是如何解析的。方便以后关于Spring是如何通过ASM 来获取类,方法,以及属性的注解。从而来实现bean的定义的创建。

本文的github地址是https://github.com/quyixiao/spring_tiny/tree/master/src/main/java/com/spring_1_100/test_31_40/test35_resource_inject

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值