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 <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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容