Java ASM详解:基础知识
由于很久没有投稿了,想了想还是发专栏吧!
之前一直在做虚拟化学实验室的程序,由于感觉不太舒服,重构了很多遍,并加入了亿点点内容,其中就有包括ASM的知识。由于网上的文章很碎,重复率也是很高,所以我打算自己来写一个。
首先第一个问题:
ASM是什么,字节码又是什么
ASM是一个Java字节码分析、创建和修改的开源应用框架。它可以动态生成二进制格式的stub类或其他代理类,或者在类被Java虚拟机装入内存之前,动态修改类。在ASM中提供了诸多的API用于对类的内容进行字节码操作的方法。与传统的BCEL和SERL不同,在ASM中提供了更为优雅和灵活的操作字节码的方式。
这是ASM官网上给出的没用解释,一句话概括:ASM是一个修改,分析Java类文件的框架。先抛开ASM框架的基本定义,先来看看字节码是什么
字节码(Byte-code)是一种包含执行程序,由一序列 op 代码/数据对组成的二进制文件
又是一段废话。从这里可以的知,字节码是一种介于翻译语言和底层语言的东西,与底层语言(C/C++)相比较你可以从它这里知道程序的运行方式,而对于翻译语言(JS)你又无法从字节码中轻易看出什么。但是由于字节码的这个特性,我们得以修改它,操纵它,并且我们还可以反编译它。
前提知识说完,开始真正的了解。


官网:https://asm.ow2.io/?FORM=UCIAST&pname=shenma
添加ASM库依赖
首先,是有关于ASM的Maven依赖
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-all</artifactId>
<version>6.0_BETA</version>
</dependency>
现在的ASM版本已经来到了6.0_BETA版本,如果你用的是asm,asm-tree这样组合也能达到相同效果。这个库以后会详细讲述,这里先说几个强有力的工具。
帮助学习ASM的工具
1.ASMifier:自动生成ASM代码
ASM库中有不少实用类,为了了解晦涩难懂的ASM代码,可以用ASMifier来进行解析。这是一个可执行类,你可以通过java.exe运行它。
运行方法:下载ASM的jar(比如asm-all-6.0_BETa.jar)或者从你的.m2文件夹里面找到它,然后运行:
java -classpath asm-all-6.0_BETA.jar org.objectweb.asm.util.ASMifier DemoDump.class
你会得到这样的输出:

2.javap.exe:Java自带的字节码解析工具
javap是你在安装JDK时就有的一个程序文件,是JDK的原生字节码解析工具,关于它的使用因篇幅有限不再细说,可以参考这篇文章 https://www.cnblogs.com/frinder6/articles/5440173.html
上方是有关于直接查看字节码的,下方则是反编译的工具
3.Eclipse插件:Enhanced Class Decompiler
这个插件是反编译器,可以在你没有库的源代码时反编译出源代码进行调试。当然,这个也可以作为你ASM程序的结果测试方案,通常反编译结果会很贴近于源代码,如果相差很大可以换一种方式反编译。

下面是有关于以后经常会用到的名词,这些对于学习ASM都极其重要。
类(型)的不同名称
类的二进制名称/类全名/简单名称
这个三个名称是等价的,也就是我们平常说的类名,例如 java.lang.Thread java.lang.Thread$UncaughtExceptionHandler这样的名称。
全限定名
这个名称是用于class文件中的名称,其实就是将二进制名称的所有"."换为"/",这个名称只有非数组引用类型才有。例如 java/lang/Thread java/io/IOException。
类型描述符
类型描述符是有关于class文件内定义字段等的类型的名称,它遵循以下规则:
1.原始类型的描述符一一对应
原始类型的描述符都对应相应的一个字母,具体来说是这样的:
byte -> B
short -> S
int -> I
long -> J
float -> F
double -> D
char -> C
void -> V
boolean -> Z
2.非数组的引用类型为 L+全限定名+;
3.数组引用类型为 [+数组内类型的描述符
例子:
java.lang.Thread -> Ljava/lang/Thread;
java.lang.Object[] -> [Ljava/lang/Object;
int[][] -> [[I
方法描述符
了解类名称和类型描述符,下面讲一下方法描述符(其实字段描述符和方法描述符统称描述符)
方法描述符是class文件中保存参数类型列表和返回值类型的方式,在各种方法调用的操作码里面都会涉及到。
规则:
1.格式为 ( + 参数列表 + ) + 返回值
2.所有类型名称都为类型描述符
3.参数列表中不需要逗号分隔
下面是抽象含义下的具体例子(省略了参数名称,只保留了参数类型):
void a(int,int,int) -> (III)V
String s(double[],boolean) ->
([DZ)Ljava/lang/String;
int[] i(Object) -> (Ljava/lang/Object;)[I
void t() -> ()V
操作码(OpCode)
Opcode是用于JVM解释运行Java程序的关键。每一个Opcode都有自己独特的含义与操作,如0x60,助记符iadd,将两个int相加。
有一点要注意:操作码其实就是一个数字,我们平时经常看到的iadd,invokestatic并不是操作码,而是助记符。
而Java中字节码的名称也与操作码有关,因为每个操作码都是用一个字节,所以叫字节码。
每一个字节用来表示一个指令,理论上可以有 256 个操作码。
对于ASM库来说,所有的Opcode都存储于org.objectweb.asm.Opcodes里面,其中还有包括它们在什么方法中作用的注释。
有关于所有操作码的网页:https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html?FORM=UCIAST&pname=shenma

这就是有关于ASM的基础知识,有些没有说到的东西会在以后的专栏中说到。
如果文章中有任何错误,可以在评论区留言,我将会修正错误。
这篇文章稍后也会在CSDN上同步。
P.S.由于这篇专栏是我用手机写的,所以没有配图,排版也有可能有问题,下一篇专栏还是用电脑写。

参考文章:
字节码-搜狗百科 https://baike.sogou.com/m/v53858822.htm
ASM 库的介绍和使用 - lijiankun24 - 简书 https://www.jianshu.com/p/905be2a9a700
全限定名、简单名称和描述符是什么东西?https://www.cnblogs.com/wxdlut/articles/12229562.html
bytecode 和 opcode 是什么?有什么区别? https://segmentfault.com/q/1010000009643588/a-1020000009643956