美文网首页
ASM框架(二)核心API 之类处理

ASM框架(二)核心API 之类处理

作者: 酒剑小仙大人 | 来源:发表于2019-07-16 00:41 被阅读0次

    一. Class结构

    1. 整体结构(Overview)

    已编译类的整体结构非常简单,一个已编译的类包含了来自源码的结构信息和几乎所有的符号信息,事实上已经编译的类包含了如下结构:

    1. 一块区域用来描述类限定符(比如private与public),类名,父类,实现的接口,与类的注解
    2. 类中每个属性都有一块区域一一对应,每个区域描述了该属性的限定符,名字,类型与注解
    3. 类中每个方法都有一块区域一一对应,每个区域描述了该方法的限定符,名字,返回类型与参数类型,注解。并且,它还以Java字节码指令序列的形式包含方法的编译代码。

    编译后的类和源码文件有如下的不同:

    • 每个编译后的类只描述一个类,然而每个源码文件可以包含多个类。举例来说一个源码文件声明了一个类,该类拥有一个内部类,这个源码文件将会被编译成两个class 文件,一个是主类的class,另一个则是内部类的class。主类文件包含对其内部类的引用,而在方法中定义的内部类包含对其封闭方法的引用
    • 编译后的类不包含注释,但是,可以包含类,字段,方法和代码的属性(attributes),属性可以将一些附加的信息关联到这些元素,自从JAVA 5 中引入了注解以来,属性变得几乎毫无用处
    • 编译后的类不包含包信息和import 区域,所有的类型名称必须是全限定名。

    另外和源码不同的是,编译后的类包含常量池区域,常量池以数组的方式存储了类中出现的数字、字符串、类型等常量。这些常量只在常量池区域中定义一次,通过常量池索引被类中其他区域所引用。ASM隐藏了所有有关常量池的细节,因此可以不用担心常量池的使用。下图展示了一个编译后的类整体结构。精确的结构描述,可以参考官方Java虚拟机规范

    已编译类整体结构预览 (* 代表一个或多个)

    2. 内部名称(Internal names)

    大部分情况下,类型(type)限制为一个类或接口的类型 。 举例来说,类的父类,和类实现的接口,或者方法抛出的异常,这些不能是基本类型或者数组类型,而一定是类或者接口。编译类中的类型(type)使用内部名称(Internal names)表示。一个类的内部名称是用 “/” 代替了 “.”的类全限定名,比如String类的内部名称为:java/lang/String

    3.类型描述符(Type descriptors)

    内部名称(Internal names)只能用来描述类和接口类型,其他情况下,比如编译类中的属性的类型,Java的类型使用类型描述符 (Type descriptors)来表述

    Java 类型描述符

    基本类型的描述符使用单个英文字符表述,类类型的描述符是该类的内部名称(Internal names),前面是L,后面是分号,比如String的类型描述符为:Ljava/lang/String; 最后,数组类型的描述符是方括号,后面跟着数组元素类型的描述符

    4.方法描述符(Method descriptors)

    方法描述符(Method descriptors) 是在由类型描述符 (Type descriptors) 组成的列表,在一个字符串中用来描述参数类型,方法返回类型。方法描述符(Method descriptors) 是由左括号开始,紧接着没一个形参的类型描述符 (Type descriptors)然后跟着右括号,紧接着是返回值的类型描述符 (Type descriptors) 或者V,如果方法返回void的话(方法描述符不包含方法的名字或者形参的名字)

    方法描述符的例子
    一旦明白了类型描述符 (Type descriptors)是怎么工作的,理解方法描述符(Method descriptors)就会变得容易,比如,(I)I描述了一个方法,拥有一个int的形参,返回值也是int,上图列出了几个方法描述符(Method descriptors) 例子

    二. JVM内部处理编译类的工具

    为了尽可能的节约存储空间,编译后的class文件往往是一个紧凑的二进制文件,基本是不可阅读的,为了解决编译后的class文件阅读
    1.javap使用
    JDK提供了javap工具,用来将编译后的Class字节码文件转换为可阅读的文本
    基本用法如下:

    格式: javap <options> <classes>
    其中, 可能的选项包括:
    -help --help -? 输出此用法消息
    -version 版本信息
    -v -verbose 输出附加信息
    -l 输出行号和本地变量表
    -public 仅显示公共类和成员
    -protected 显示受保护的/公共类和成员
    -package 显示程序包/受保护的/公共类
    和成员 (默认)
    -p -private 显示所有类和成员
    -c 对代码进行反汇编
    -s 输出内部类型签名
    -sysinfo 显示正在处理的类的
    系统信息 (路径, 大小, 日期, MD5 散列)
    -constants 显示最终常量
    -classpath <path> 指定查找用户类文件的位置
    -cp <path> 指定查找用户类文件的位置
    -bootclasspath <path> 覆盖引导类文件的位置

    示例代码

    package asm;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    
    public class ExampleClass<T extends Map> {
        /**
         * 静态属性
         */
        public static String staticField = "staticField";
        /**
         * 实例属性
         */
        public String field = "";
        /**
         * 泛型属性
         */
        public T map = null;
        /**
         * 构造函数
         */
        public ExampleClass(String field){
            this.field = field;
        }
        /**
         * 包含内部类的方法
         */
        public Runnable getRuuable(){
                return new Runnable(){
                    @Override
                    public void run() {
    
                    }
                };
        }
        /**
         * 静态方法
         */
        public static String  staticMethod(String param2){
            System.out.println(param2);
            return param2;
        }
        /**
         * 实例方法
         */
        private void instanceMehtod(int param1,String param2){
            System.out.println(param2 + param1);
        }
        /**
         * 操作泛型实例的方法
         */
        private void genericityMethod1(){
            int size = map.size();
            System.out.println(size);
        }
        /**
         * 泛型方法1
         */
        private T getMap(){
            return map;
        }
        /**
         * 泛型方法2
         */
        public <P> P genericMethod(Class<P> tClass)throws InstantiationException ,
                IllegalAccessException{
            P instance = tClass.newInstance();
            return instance;
        }
        /**
         * lambda 表达式
         */
        public void lambda(){
            new Thread(()->{
                System.out.println("lambad");
            });
        }
        /**
         * 方法函数
         */
        public void methodFuntion(){
            List<String> al = Arrays.asList("a", "b", "c", "d");
            al.forEach(ExampleClass::staticMethod);
        }
    }
    

    编译后会生成 ExampleClass.class文件,执行

    javap -verbose asm.ExampleClass

    我们可以看到阅读友好的编译后的class文件

    Classfile /Users/zhuangpeng/Documents/kaola/dubbo.test/target/classes/asm/ExampleClass.class
      Last modified 2019-7-21; size 3509 bytes
      MD5 checksum b347b3698dbc10d4b13927d1bf763a13
      Compiled from "ExampleClass.java"
    public class asm.ExampleClass<T extends java.util.Map> extends java.lang.Object
      minor version: 0
      major version: 52
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
        #1 = Methodref          #32.#87       // java/lang/Object."<init>":()V
        #2 = String             #88           //
        #3 = Fieldref           #31.#89       // asm/ExampleClass.field:Ljava/lang/String;
        #4 = Fieldref           #31.#90       // asm/ExampleClass.map:Ljava/util/Map;
        #5 = Class              #91           // asm/ExampleClass$1
        #6 = Methodref          #5.#92        // asm/ExampleClass$1."<init>":(Lasm/ExampleClass;)V
        #7 = Fieldref           #93.#94       // java/lang/System.out:Ljava/io/PrintStream;
        #8 = Methodref          #95.#96       // java/io/PrintStream.println:(Ljava/lang/String;)V
        #9 = Class              #97           // java/lang/StringBuilder
       #10 = Methodref          #9.#87        // java/lang/StringBuilder."<init>":()V
       #11 = Methodref          #9.#98        // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       #12 = Methodref          #9.#99        // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
       #13 = Methodref          #9.#100       // java/lang/StringBuilder.toString:()Ljava/lang/String;
       #14 = InterfaceMethodref #101.#102     // java/util/Map.size:()I
       #15 = Methodref          #95.#103      // java/io/PrintStream.println:(I)V
       #16 = Methodref          #104.#105     // java/lang/Class.newInstance:()Ljava/lang/Object;
       #17 = Class              #106          // java/lang/Thread
       #18 = InvokeDynamic      #0:#111       // #0:run:()Ljava/lang/Runnable;
       #19 = Methodref          #17.#112      // java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
       #20 = Class              #113          // java/lang/String
       #21 = String             #114          // a
       #22 = String             #115          // b
       #23 = String             #116          // c
       #24 = String             #117          // d
       #25 = Methodref          #118.#119     // java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
       #26 = InvokeDynamic      #1:#123       // #1:accept:()Ljava/util/function/Consumer;
       #27 = InterfaceMethodref #124.#125     // java/util/List.forEach:(Ljava/util/function/Consumer;)V
       #28 = String             #126          // lambad
       #29 = String             #34           // staticField
       #30 = Fieldref           #31.#127      // asm/ExampleClass.staticField:Ljava/lang/String;
       #31 = Class              #128          // asm/ExampleClass
       #32 = Class              #129          // java/lang/Object
       #33 = Utf8               InnerClasses
       #34 = Utf8               staticField
       #35 = Utf8               Ljava/lang/String;
       #36 = Utf8               field
       #37 = Utf8               map
       #38 = Utf8               Ljava/util/Map;
       #39 = Utf8               Signature
       #40 = Utf8               TT;
       #41 = Utf8               <init>
       #42 = Utf8               (Ljava/lang/String;)V
       #43 = Utf8               Code
       #44 = Utf8               LineNumberTable
       #45 = Utf8               LocalVariableTable
       #46 = Utf8               this
       #47 = Utf8               Lasm/ExampleClass;
       #48 = Utf8               LocalVariableTypeTable
       #49 = Utf8               Lasm/ExampleClass<TT;>;
       #50 = Utf8               getRuuable
       #51 = Utf8               ()Ljava/lang/Runnable;
       #52 = Utf8               staticMethod
       #53 = Utf8               (Ljava/lang/String;)Ljava/lang/String;
       #54 = Utf8               param2
       #55 = Utf8               instanceMehtod
       #56 = Utf8               (ILjava/lang/String;)V
       #57 = Utf8               param1
       #58 = Utf8               I
       #59 = Utf8               genericityMethod1
       #60 = Utf8               ()V
       #61 = Utf8               size
       #62 = Utf8               getMap
       #63 = Utf8               ()Ljava/util/Map;
       #64 = Utf8               ()TT;
       #65 = Utf8               genericMethod
       #66 = Utf8               (Ljava/lang/Class;)Ljava/lang/Object;
       #67 = Utf8               tClass
       #68 = Utf8               Ljava/lang/Class;
       #69 = Utf8               instance
       #70 = Utf8               Ljava/lang/Object;
       #71 = Utf8               Ljava/lang/Class<TP;>;
       #72 = Utf8               TP;
       #73 = Utf8               Exceptions
       #74 = Class              #130          // java/lang/InstantiationException
       #75 = Class              #131          // java/lang/IllegalAccessException
       #76 = Utf8               <P:Ljava/lang/Object;>(Ljava/lang/Class<TP;>;)TP;
       #77 = Utf8               lambda
       #78 = Utf8               methodFuntion
       #79 = Utf8               al
       #80 = Utf8               Ljava/util/List;
       #81 = Utf8               Ljava/util/List<Ljava/lang/String;>;
       #82 = Utf8               lambda$lambda$0
       #83 = Utf8               <clinit>
       #84 = Utf8               <T::Ljava/util/Map;>Ljava/lang/Object;
       #85 = Utf8               SourceFile
       #86 = Utf8               ExampleClass.java
       #87 = NameAndType        #41:#60       // "<init>":()V
       #88 = Utf8
       #89 = NameAndType        #36:#35       // field:Ljava/lang/String;
       #90 = NameAndType        #37:#38       // map:Ljava/util/Map;
       #91 = Utf8               asm/ExampleClass$1
       #92 = NameAndType        #41:#132      // "<init>":(Lasm/ExampleClass;)V
       #93 = Class              #133          // java/lang/System
       #94 = NameAndType        #134:#135     // out:Ljava/io/PrintStream;
       #95 = Class              #136          // java/io/PrintStream
       #96 = NameAndType        #137:#42      // println:(Ljava/lang/String;)V
       #97 = Utf8               java/lang/StringBuilder
       #98 = NameAndType        #138:#139     // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       #99 = NameAndType        #138:#140     // append:(I)Ljava/lang/StringBuilder;
      #100 = NameAndType        #141:#142     // toString:()Ljava/lang/String;
      #101 = Class              #143          // java/util/Map
      #102 = NameAndType        #61:#144      // size:()I
      #103 = NameAndType        #137:#145     // println:(I)V
      #104 = Class              #146          // java/lang/Class
      #105 = NameAndType        #147:#148     // newInstance:()Ljava/lang/Object;
      #106 = Utf8               java/lang/Thread
      #107 = Utf8               BootstrapMethods
      #108 = MethodHandle       #6:#149       // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      #109 = MethodType         #60           //  ()V
      #110 = MethodHandle       #6:#150       // invokestatic asm/ExampleClass.lambda$lambda$0:()V
      #111 = NameAndType        #151:#51      // run:()Ljava/lang/Runnable;
      #112 = NameAndType        #41:#152      // "<init>":(Ljava/lang/Runnable;)V
      #113 = Utf8               java/lang/String
      #114 = Utf8               a
      #115 = Utf8               b
      #116 = Utf8               c
      #117 = Utf8               d
      #118 = Class              #153          // java/util/Arrays
      #119 = NameAndType        #154:#155     // asList:([Ljava/lang/Object;)Ljava/util/List;
      #120 = MethodType         #156          //  (Ljava/lang/Object;)V
      #121 = MethodHandle       #6:#157       // invokestatic asm/ExampleClass.staticMethod:(Ljava/lang/String;)Ljava/lang/String;
      #122 = MethodType         #42           //  (Ljava/lang/String;)V
      #123 = NameAndType        #158:#159     // accept:()Ljava/util/function/Consumer;
      #124 = Class              #160          // java/util/List
      #125 = NameAndType        #161:#162     // forEach:(Ljava/util/function/Consumer;)V
      #126 = Utf8               lambad
      #127 = NameAndType        #34:#35       // staticField:Ljava/lang/String;
      #128 = Utf8               asm/ExampleClass
      #129 = Utf8               java/lang/Object
      #130 = Utf8               java/lang/InstantiationException
      #131 = Utf8               java/lang/IllegalAccessException
      #132 = Utf8               (Lasm/ExampleClass;)V
      #133 = Utf8               java/lang/System
      #134 = Utf8               out
      #135 = Utf8               Ljava/io/PrintStream;
      #136 = Utf8               java/io/PrintStream
      #137 = Utf8               println
      #138 = Utf8               append
      #139 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
      #140 = Utf8               (I)Ljava/lang/StringBuilder;
      #141 = Utf8               toString
      #142 = Utf8               ()Ljava/lang/String;
      #143 = Utf8               java/util/Map
      #144 = Utf8               ()I
      #145 = Utf8               (I)V
      #146 = Utf8               java/lang/Class
      #147 = Utf8               newInstance
      #148 = Utf8               ()Ljava/lang/Object;
      #149 = Methodref          #163.#164     // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      #150 = Methodref          #31.#165      // asm/ExampleClass.lambda$lambda$0:()V
      #151 = Utf8               run
      #152 = Utf8               (Ljava/lang/Runnable;)V
      #153 = Utf8               java/util/Arrays
      #154 = Utf8               asList
      #155 = Utf8               ([Ljava/lang/Object;)Ljava/util/List;
      #156 = Utf8               (Ljava/lang/Object;)V
      #157 = Methodref          #31.#166      // asm/ExampleClass.staticMethod:(Ljava/lang/String;)Ljava/lang/String;
      #158 = Utf8               accept
      #159 = Utf8               ()Ljava/util/function/Consumer;
      #160 = Utf8               java/util/List
      #161 = Utf8               forEach
      #162 = Utf8               (Ljava/util/function/Consumer;)V
      #163 = Class              #167          // java/lang/invoke/LambdaMetafactory
      #164 = NameAndType        #168:#171     // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      #165 = NameAndType        #82:#60       // lambda$lambda$0:()V
      #166 = NameAndType        #52:#53       // staticMethod:(Ljava/lang/String;)Ljava/lang/String;
      #167 = Utf8               java/lang/invoke/LambdaMetafactory
      #168 = Utf8               metafactory
      #169 = Class              #173          // java/lang/invoke/MethodHandles$Lookup
      #170 = Utf8               Lookup
      #171 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      #172 = Class              #174          // java/lang/invoke/MethodHandles
      #173 = Utf8               java/lang/invoke/MethodHandles$Lookup
      #174 = Utf8               java/lang/invoke/MethodHandles
    {
      public static java.lang.String staticField;
        descriptor: Ljava/lang/String;
        flags: ACC_PUBLIC, ACC_STATIC
    
      public java.lang.String field;
        descriptor: Ljava/lang/String;
        flags: ACC_PUBLIC
    
      public T map;
        descriptor: Ljava/util/Map;
        flags: ACC_PUBLIC
        Signature: #40                          // TT;
    
      public asm.ExampleClass(java.lang.String);
        descriptor: (Ljava/lang/String;)V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=2, args_size=2
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: aload_0
             5: ldc           #2                  // String
             7: putfield      #3                  // Field field:Ljava/lang/String;
            10: aload_0
            11: aconst_null
            12: putfield      #4                  // Field map:Ljava/util/Map;
            15: aload_0
            16: aload_1
            17: putfield      #3                  // Field field:Ljava/lang/String;
            20: return
          LineNumberTable:
            line 31: 0
            line 21: 4
            line 26: 10
            line 32: 15
            line 33: 20
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      21     0  this   Lasm/ExampleClass;
                0      21     1 field   Ljava/lang/String;
          LocalVariableTypeTable:
            Start  Length  Slot  Name   Signature
                0      21     0  this   Lasm/ExampleClass<TT;>;
    
      public java.lang.Runnable getRuuable();
        descriptor: ()Ljava/lang/Runnable;
        flags: ACC_PUBLIC
        Code:
          stack=3, locals=1, args_size=1
             0: new           #5                  // class asm/ExampleClass$1
             3: dup
             4: aload_0
             5: invokespecial #6                  // Method asm/ExampleClass$1."<init>":(Lasm/ExampleClass;)V
             8: areturn
          LineNumberTable:
            line 39: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       9     0  this   Lasm/ExampleClass;
          LocalVariableTypeTable:
            Start  Length  Slot  Name   Signature
                0       9     0  this   Lasm/ExampleClass<TT;>;
    
      public static java.lang.String staticMethod(java.lang.String);
        descriptor: (Ljava/lang/String;)Ljava/lang/String;
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=1, args_size=1
             0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: aload_0
             4: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             7: aload_0
             8: areturn
          LineNumberTable:
            line 51: 0
            line 52: 7
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       9     0 param2   Ljava/lang/String;
    
      private void instanceMehtod(int, java.lang.String);
        descriptor: (ILjava/lang/String;)V
        flags: ACC_PRIVATE
        Code:
          stack=3, locals=3, args_size=3
             0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: new           #9                  // class java/lang/StringBuilder
             6: dup
             7: invokespecial #10                 // Method java/lang/StringBuilder."<init>":()V
            10: aload_2
            11: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            14: iload_1
            15: invokevirtual #12                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
            18: invokevirtual #13                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
            21: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            24: return
          LineNumberTable:
            line 61: 0
            line 62: 24
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      25     0  this   Lasm/ExampleClass;
                0      25     1 param1   I
                0      25     2 param2   Ljava/lang/String;
          LocalVariableTypeTable:
            Start  Length  Slot  Name   Signature
                0      25     0  this   Lasm/ExampleClass<TT;>;
    
      private void genericityMethod1();
        descriptor: ()V
        flags: ACC_PRIVATE
        Code:
          stack=2, locals=2, args_size=1
             0: aload_0
             1: getfield      #4                  // Field map:Ljava/util/Map;
             4: invokeinterface #14,  1           // InterfaceMethod java/util/Map.size:()I
             9: istore_1
            10: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
            13: iload_1
            14: invokevirtual #15                 // Method java/io/PrintStream.println:(I)V
            17: return
          LineNumberTable:
            line 69: 0
            line 70: 10
            line 71: 17
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      18     0  this   Lasm/ExampleClass;
               10       8     1  size   I
          LocalVariableTypeTable:
            Start  Length  Slot  Name   Signature
                0      18     0  this   Lasm/ExampleClass<TT;>;
    
      private T getMap();
        descriptor: ()Ljava/util/Map;
        flags: ACC_PRIVATE
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: getfield      #4                  // Field map:Ljava/util/Map;
             4: areturn
          LineNumberTable:
            line 79: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lasm/ExampleClass;
          LocalVariableTypeTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lasm/ExampleClass<TT;>;
        Signature: #64                          // ()TT;
    
      public <P extends java.lang.Object> P genericMethod(java.lang.Class<P>) throws java.lang.InstantiationException, java.lang.IllegalAccessException;
        descriptor: (Ljava/lang/Class;)Ljava/lang/Object;
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=3, args_size=2
             0: aload_1
             1: invokevirtual #16                 // Method java/lang/Class.newInstance:()Ljava/lang/Object;
             4: astore_2
             5: aload_2
             6: areturn
          LineNumberTable:
            line 88: 0
            line 89: 5
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       7     0  this   Lasm/ExampleClass;
                0       7     1 tClass   Ljava/lang/Class;
                5       2     2 instance   Ljava/lang/Object;
          LocalVariableTypeTable:
            Start  Length  Slot  Name   Signature
                0       7     0  this   Lasm/ExampleClass<TT;>;
                0       7     1 tClass   Ljava/lang/Class<TP;>;
                5       2     2 instance   TP;
        Exceptions:
          throws java.lang.InstantiationException, java.lang.IllegalAccessException
        Signature: #76                          // <P:Ljava/lang/Object;>(Ljava/lang/Class<TP;>;)TP;
    
      public void lambda();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=3, locals=1, args_size=1
             0: new           #17                 // class java/lang/Thread
             3: dup
             4: invokedynamic #18,  0             // InvokeDynamic #0:run:()Ljava/lang/Runnable;
             9: invokespecial #19                 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
            12: pop
            13: return
          LineNumberTable:
            line 97: 0
            line 100: 13
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      14     0  this   Lasm/ExampleClass;
          LocalVariableTypeTable:
            Start  Length  Slot  Name   Signature
                0      14     0  this   Lasm/ExampleClass<TT;>;
    
      public void methodFuntion();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=4, locals=2, args_size=1
             0: iconst_4
             1: anewarray     #20                 // class java/lang/String
             4: dup
             5: iconst_0
             6: ldc           #21                 // String a
             8: aastore
             9: dup
            10: iconst_1
            11: ldc           #22                 // String b
            13: aastore
            14: dup
            15: iconst_2
            16: ldc           #23                 // String c
            18: aastore
            19: dup
            20: iconst_3
            21: ldc           #24                 // String d
            23: aastore
            24: invokestatic  #25                 // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
            27: astore_1
            28: aload_1
            29: invokedynamic #26,  0             // InvokeDynamic #1:accept:()Ljava/util/function/Consumer;
            34: invokeinterface #27,  2           // InterfaceMethod java/util/List.forEach:(Ljava/util/function/Consumer;)V
            39: return
          LineNumberTable:
            line 107: 0
            line 108: 28
            line 109: 39
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      40     0  this   Lasm/ExampleClass;
               28      12     1    al   Ljava/util/List;
          LocalVariableTypeTable:
            Start  Length  Slot  Name   Signature
                0      40     0  this   Lasm/ExampleClass<TT;>;
               28      12     1    al   Ljava/util/List<Ljava/lang/String;>;
    
      private static void lambda$lambda$0();
        descriptor: ()V
        flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
        Code:
          stack=2, locals=0, args_size=0
             0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: ldc           #28                 // String lambad
             5: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             8: return
          LineNumberTable:
            line 98: 0
            line 99: 8
    
      static {};
        descriptor: ()V
        flags: ACC_STATIC
        Code:
          stack=1, locals=0, args_size=0
             0: ldc           #29                 // String staticField
             2: putstatic     #30                 // Field staticField:Ljava/lang/String;
             5: return
          LineNumberTable:
            line 16: 0
    }
    Signature: #84                          // <T::Ljava/util/Map;>Ljava/lang/Object;
    SourceFile: "ExampleClass.java"
    InnerClasses:
         #5; //class asm/ExampleClass$1
         public static final #170= #169 of #172; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
    BootstrapMethods:
      0: #108 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
        Method arguments:
          #109 ()V
          #110 invokestatic asm/ExampleClass.lambda$lambda$0:()V
          #109 ()V
      1: #108 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
        Method arguments:
          #120 (Ljava/lang/Object;)V
          #121 invokestatic asm/ExampleClass.staticMethod:(Ljava/lang/String;)Ljava/lang/String;
          #122 (Ljava/lang/String;)V
    

    编译后的文件内容一一对应本文Class结构
    更详尽的编译后的类文件的结构和作用参考Java虚拟机规范 第四章Class 文件结构
    中文版的参考资料:深入理解Java虚拟机:JVM高级特性与最佳实践

    三. ASM类处理接口与组件

    1. 概述
      ASM基于ClassVisitor 这个抽象类来生产和转换编译后类,这个类的每一个方法都对应着java文件结构的一片同名的区域。简单的区域使用简单的方法来访问,方法的参数描述了这个区域的内容,并且返回void。一些区域包含了任意长度且复杂的内容,使用初始化方法返回的辅助类来访问,比如visitAnnotationvisitFieldvisitMethod等方法,分别返回了AnnotationVisitorFieldVisitorMethodVisitor,对于这些辅助类,递归地使用相同的原则,比如FieldVisitor抽象类中的每个方法都对应于相同名字的类的子结构,像ClassVisitor返回一个辅助的AnnotationVisitor。
      ClassVisitor 类结构
      有关辅助类的用法,后续会讲到,本章限定一些只需要ClassVisitor 就能解决的简单问题。

    ClassVisitor必须按照Class的Javadoc中指定的顺序来调用

    visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )* ( visitInnerClass | visitField | visitMethod )*
    visitEnd
    

    这表明,visit必须最先调用,然后紧跟着最多一次调用的visitSource,接着最多一次调用的visitOuterClass,接着任意顺序任意数量调用的visitAnnotationvisitAttribute,接着任意顺序和数量的visitInnerClassvisitFieldvisitMethod ,最后以 visitEnd调用结束。
    ASM提供了三个基于ClassVisitor API的核心组件用来生成和转换类

    • ClassReader 类接受编译后的类的字节数组,然后调用作为参数传入accept 方法的 ClassVisitor实例的visitXXX 系列方法,可能看作是事件生产者
    • ClassWriter 是 抽象类ClassVisitor 的一个子类,能够直接以二进制数据的形式组装编译好的类。它生产出包含了编译好的类的字节数组,可以通过toByteArray来获取,可以看作是事件的消费者
    • ClassVisitor类将它接收到的所有方法调用委托给另一个ClassVisitor实例。它可以看作是一个事件过滤器。
      接下来,我们看这三个组件如何配合使用
    1. 类解析
      解析一个类唯一必须需要的组件是ClassReader。假设我们想要打印一个编译类的内容,就像javap工具这样,第一件事就是实现一个主要功能是用来的打印类信息的ClassVisitor的子类。这里给出一个类打印子类的的例子
      第一步编写打印类
    package asm;
    
    import org.objectweb.asm.*;
    import org.objectweb.asm.util.TraceClassVisitor;
    
    import java.io.IOException;
    
    public class ClassPrinter extends ClassVisitor {
    
    
        public ClassPrinter() {
            super(Opcodes.ASM7);
        }
    
        @Override
        public void visit(
                final int version,
                final int access,
                final String name,
                final String signature,
                final String superName,
                final String[] interfaces) {
            System.out.println(name + " extends " + superName + " {");
        }
    
        @Override
        public void visitSource(String source, String debug) {
    
        }
    
        @Override
        public void visitOuterClass(String owner, String name, String desc) {
            System.out.println(owner +"---" + name + "---" +desc);
        }
        @Override
        public AnnotationVisitor visitAnnotation(String desc,
                                                 boolean visible) {
            return null;
        }
        @Override
        public void visitAttribute(Attribute attr) {
        }
        @Override
        public void visitInnerClass(String name, String outerName,
                                    String innerName, int access) {
        }
        @Override
        public FieldVisitor visitField(int access, String name, String desc,
                                       String signature, Object value) {
            System.out.println("    " + desc + " " + name);
            return null;
        }
        @Override
        public MethodVisitor visitMethod(int access, String name,
                                         String desc, String signature, String[] exceptions){
            System.out.println("    " + name + " " + desc);
            return null;
        }
        @Override
        public void visitEnd() {
             System.out.println("}");
        }
    
        public static void main(String args[]) throws IOException {
    
            ClassReader cr = null;
            ClassPrinter writer = null;
            cr = new ClassReader("asm.ExampleClass");
            writer = new ClassPrinter();
            cr.accept(writer, 0);
        }
    }
    
    

    第二步将ClassPrinterClassReader结合起来,这样ClassPrinter就能消费到ClassReader产生的事件

            ClassReader cr = new ClassReader("asm.ExampleClass");
            ClassPrinter writer = new ClassPrinter();
            cr.accept(writer, 0);
    

    accept方法解析了传入的类的字节码,然后通过writer调用了ClassPrinter对应的方法,执行结果如下,为了简化例子,只打印一些方法名等信息

    asm/ExampleClass extends java/lang/Object {
        Ljava/lang/String; staticField
        Ljava/lang/String; field
        Ljava/util/Map; map
        <init> (Ljava/lang/String;)V
        getRuuable ()Ljava/lang/Runnable;
        staticMethod (Ljava/lang/String;)Ljava/lang/String;
        instanceMehtod (ILjava/lang/String;)V
        genericityMethod1 ()V
        getMap ()Ljava/util/Map;
        genericMethod (Ljava/lang/Class;)Ljava/lang/Object;
        lambda ()V
        demoMethod (Ljava/lang/String;)Ljava/lang/String;
        methodFuntion ()V
        lambda$lambda$0 ()V
        <clinit> ()V
    }
    

    注意 ClassReader 有若干种构造方法.像例子中这样,传入一个类的全限定名称,或者传入编译后类的字节数组,或者传入InputStream输入流。可以通过ClassLoader的 getResourceAsStream来获得一个编译后类的输入流

    cl.getResourceAsStream(classname.replace(’.’, ’/’) + ".class");

    3.生产类
    //TODO

    相关文章

      网友评论

          本文标题:ASM框架(二)核心API 之类处理

          本文链接:https://www.haomeiwen.com/subject/gzoywqtx.html