一. Class结构
1. 整体结构(Overview)
已编译类的整体结构非常简单,一个已编译的类包含了来自源码的结构信息和几乎所有的符号信息,事实上已经编译的类包含了如下结构:
- 一块区域用来描述类限定符(比如private与public),类名,父类,实现的接口,与类的注解
- 类中每个属性都有一块区域一一对应,每个区域描述了该属性的限定符,名字,类型与注解
- 类中每个方法都有一块区域一一对应,每个区域描述了该方法的限定符,名字,返回类型与参数类型,注解。并且,它还以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)来表述
基本类型的描述符使用单个英文字符表述,类类型的描述符是该类的内部名称(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类处理接口与组件
- 概述
ASM基于ClassVisitor 这个抽象类来生产和转换编译后类,这个类的每一个方法都对应着java文件结构的一片同名的区域。简单的区域使用简单的方法来访问,方法的参数描述了这个区域的内容,并且返回void。一些区域包含了任意长度且复杂的内容,使用初始化方法返回的辅助类来访问,比如visitAnnotation,visitField和visitMethod等方法,分别返回了AnnotationVisitor,FieldVisitor,MethodVisitor,对于这些辅助类,递归地使用相同的原则,比如FieldVisitor抽象类中的每个方法都对应于相同名字的类的子结构,像ClassVisitor返回一个辅助的AnnotationVisitor。
有关辅助类的用法,后续会讲到,本章限定一些只需要ClassVisitor 就能解决的简单问题。
ClassVisitor必须按照Class的Javadoc中指定的顺序来调用
visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )* ( visitInnerClass | visitField | visitMethod )*
visitEnd
这表明,visit必须最先调用,然后紧跟着最多一次调用的visitSource,接着最多一次调用的visitOuterClass,接着任意顺序任意数量调用的visitAnnotation和visitAttribute,接着任意顺序和数量的visitInnerClass、visitField 、visitMethod ,最后以 visitEnd调用结束。
ASM提供了三个基于ClassVisitor API的核心组件用来生成和转换类
- ClassReader 类接受编译后的类的字节数组,然后调用作为参数传入accept 方法的 ClassVisitor实例的visitXXX 系列方法,可能看作是事件生产者
- ClassWriter 是 抽象类ClassVisitor 的一个子类,能够直接以二进制数据的形式组装编译好的类。它生产出包含了编译好的类的字节数组,可以通过toByteArray来获取,可以看作是事件的消费者
- ClassVisitor类将它接收到的所有方法调用委托给另一个ClassVisitor实例。它可以看作是一个事件过滤器。
接下来,我们看这三个组件如何配合使用
- 类解析
解析一个类唯一必须需要的组件是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);
}
}
第二步将ClassPrinter和ClassReader结合起来,这样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