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

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

示例代码

package asm;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class ExampleClass {
    /**
     * 静态属性
     */
    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 genericMethod(Class

tClass)throws InstantiationException , IllegalAccessException{ P instance = tClass.newInstance(); return instance; } /** * lambda 表达式 */ public void lambda(){ new Thread(()->{ System.out.println("lambad"); }); } /** * 方法函数 */ public void methodFuntion(){ List 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 extends java.lang.Object
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
    #1 = Methodref          #32.#87       // java/lang/Object."":()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."":(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."":()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."":(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               
   #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;
   #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;
   #72 = Utf8               TP;
   #73 = Utf8               Exceptions
   #74 = Class              #130          // java/lang/InstantiationException
   #75 = Class              #131          // java/lang/IllegalAccessException
   #76 = Utf8               (Ljava/lang/Class;)TP;
   #77 = Utf8               lambda
   #78 = Utf8               methodFuntion
   #79 = Utf8               al
   #80 = Utf8               Ljava/util/List;
   #81 = Utf8               Ljava/util/List;
   #82 = Utf8               lambda$lambda$0
   #83 = Utf8               
   #84 = Utf8               Ljava/lang/Object;
   #85 = Utf8               SourceFile
   #86 = Utf8               ExampleClass.java
   #87 = NameAndType        #41:#60       // "":()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      // "":(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      // "":(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."":()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;

  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."":(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;

  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."":()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;

  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;

  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;
    Signature: #64                          // ()TT;

  public 

P genericMethod(java.lang.Class

) 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; 0 7 1 tClass Ljava/lang/Class; 5 2 2 instance TP; Exceptions: throws java.lang.InstantiationException, java.lang.IllegalAccessException Signature: #76 // (Ljava/lang/Class;)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."":(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; 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; 28 12 1 al Ljava/util/List; 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 // 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
     (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
     ()V
}

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

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

3.生产类
//TODO

你可能感兴趣的:(ASM框架(二)核心API 之类处理)