前言
什么是ASM?ASM是一款小巧精致的Java字节码操作工具,可以被用于修改已经存在的类和生成一些还不存在的类。ASM被广泛的应用于OpenJDK、Jacoco、CGLIB等工具。
一、Class编译形态
我们平时在Eclipse里面写的.java文件Eclipse已经帮我们编译好了,实际上编译是有规则的,编译好的类是按照一定规则被编译器生成的,包括类的定义、方法的定义、成员变量的定义规则等等。虚拟机中最后工作的类就是通过ClassLoader把这些编译好的类通过读字节流的形式读取并解析成内存中的可用的类的。
1、Complied Class结构
已编译的类结构如下图,*表示可有可无。实际上这里省略了魔数等虚拟机要求定义的一些成员留下了最关键的类结构信息。
![12062369-ea47918e0ede7575.png](https://i-blog.csdnimg.cn/blog_migrate/aaf6f8cccc3b26c5d28e9c358cf728cc.webp?x-image-process=image/format,png)
1.1、Complied Class类型和方法描述符
已编译的类中,描述字段和方法都有特定的描述符,所有的类型描述符如下图,对于普通类型每个类型编译之后对应的都是一个大写字母;对于对象类型使用L开头反斜杠做分隔符描述对象;对于数组类型,通过[开头,结合内部使用的类型构成数组的描述。
![12062369-3eb6af8f3f3f81aa.png](https://i-blog.csdnimg.cn/blog_migrate/6f919f806c30a9e88e049d78fc8f8fb6.webp?x-image-process=image/format,png)
一个方法被编译之后就会使用方法描述符来描述,小括号里面表示参数类型和参数数量,小括号外面表示返回类型。实际上在已编译类中,返回类型不同的方法也是不同的方法,只不过Java不允许这种方法重载。
![12062369-4dd3e99d158ae6b6.png](https://i-blog.csdnimg.cn/blog_migrate/370c786a0486e4bdadd7f3e2e3d1ad4d.webp?x-image-process=image/format,png)
1.2、Complied Class中的方法体描述
上面的描述符只描述了方法签名,但是一些方法内部的变量,操作都没有描述,要想了解这些我们首先需要了解Java虚拟机模型。
简单来说,Java中每个线程都有自己的栈空间,而方法开始执行的时候会以帧的形式被压入到线程的栈中,方法执行结束(正常返回和抛异常)又从栈中被弹出。描述方法的帧是由局部变量表和操作数栈组成的,前者保存我们定义的int a,int b这种变量,后者保存真正的操作数,如a=1,就保存1。局部变量表中的位置叫做槽位,对于Long和double等长字节类型需要占用两个槽位,其余占用一个槽位。操作数栈中的操作数可以通过字节码指令操作。每个帧被压入线程栈的时候都会被分配该帧私有的操作数栈和局部变量空间,然后由线程在操作数栈通过字节码指令进行相关操作,这就是方法的执行。
![12062369-5e6a887932c289d9.png](https://i-blog.csdnimg.cn/blog_migrate/d500acaacb8aba5a2b56cdfe3a1eb298.webp?x-image-process=image/format,png)
1.2.1、字节码指令
字节码指令由操作码和参数组成,字节码指令工作的主要场所就是操作数栈。
常用操作码有两类,一类用于操作变量,另一类用于操作数栈本身
- 操作变量操作码:ILOAD, LLOAD, FLOAD, DLOAD 和 ALOAD 指令读取一个局部变量,并将它的值压到操
作数栈中。它们的参数是必须读取的局部变量的索引 i。 ILOAD 用于加载一个 boolean、 byte、char、short 或 int 局部变量。LLOAD、FLOAD 和 DLOAD 分别用于加载 long、float 或 double值。( LLOAD 和 DLOAD 实际加载两个槽 i 和 i+1)。最后, ALOAD 用于加载任意非基元值,即对象和数组引用。与之对应ISTORE、 LSTORE、 FSTORE、 DSTORE 和 ASTORE 指令从操作数栈中弹出一个值,并将它存储在由其索引 i