ASM 是 Java 字节码操作和分析的框架,它提供了一套基于访问者模式的 API,允许你在编译后直接修改类文件。以下是我梳理的核心指令及其使用方法:
一、核心指令分类与用法
1. 类结构指令
| 指令 | 作用 | 使用示例 |
|---|
visit | 访问类定义 | cv.visit(Opcodes.V1_8, ACC_PUBLIC, "com/Example", null, "java/lang/Object", null) |
visitField | 添加字段 | cv.visitField(ACC_PRIVATE, "count", "I", null, null) |
visitMethod | 添加方法 | cv.visitMethod(ACC_PUBLIC, "calculate", "(II)I", null, null) |
visitEnd | 结束类访问 | cv.visitEnd() |
2. 方法操作指令
(1) 局部变量操作
| 指令 | 作用 | 使用示例 | 对应字节码 |
|---|
ILOAD | 加载int变量 | mv.visitVarInsn(ILOAD, 1) | iload_1 |
ALOAD | 加载对象引用 | mv.visitVarInsn(ALOAD, 0) | aload_0 |
ISTORE | 存储int值 | mv.visitVarInsn(ISTORE, 2) | istore_2 |
ASTORE | 存储对象引用 | mv.visitVarInsn(ASTORE, 3) | astore_3 |
(2) 常量加载
| 指令 | 作用 | 使用示例 | 对应字节码 |
|---|
ICONST_0 | 加载int常量0 | mv.visitInsn(ICONST_0) | iconst_0 |
BIPUSH | 加载字节常量 | mv.visitIntInsn(BIPUSH, 100) | bipush 100 |
LDC | 加载复杂常量 | mv.visitLdcInsn("Hello World") | ldc "Hello World" |
ACONST_NULL | 加载null | mv.visitInsn(ACONST_NULL) | aconst_null |
(3) 算术运算
| 指令 | 作用 | 使用示例 | 示例代码对应关系 |
|---|
IADD | int加法 | mv.visitInsn(IADD) | int c = a + b; |
ISUB | int减法 | mv.visitInsn(ISUB) | int c = a - b; |
IMUL | int乘法 | mv.visitInsn(IMUL) | int c = a * b; |
IDIV | int除法 | mv.visitInsn(IDIV) | int c = a / b; |
(4) 控制流
| 指令 | 作用 | 使用示例 | 说明 |
|---|
IFEQ | 等于0跳转 | mv.visitJumpInsn(IFEQ, label) | if (value == 0) |
IFNE | 不等于0跳转 | mv.visitJumpInsn(IFNE, label) | if (value != 0) |
IF_ICMPEQ | int相等跳转 | mv.visitJumpInsn(IF_ICMPEQ, label) | if (a == b) |
GOTO | 无条件跳转 | mv.visitJumpInsn(GOTO, endLabel) | break/continue |
Label | 定义跳转目标 | Label start = new Label() | 配合跳转指令使用 |
(5) 方法调用
| 指令 | 作用 | 使用示例 |
|---|
INVOKEVIRTUAL | 调用实例方法 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false) |
INVOKESTATIC | 调用静态方法 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Math", "max", "(II)I", false) |
INVOKESPECIAL | 调用构造方法/父类方法 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false) |
(6) 返回指令
| 指令 | 作用 | 使用示例 |
|---|
RETURN | 返回void | mv.visitInsn(RETURN) |
IRETURN | 返回int | mv.visitInsn(IRETURN) |
ARETURN | 返回对象引用 | mv.visitInsn(ARETURN) |
3. 字段操作指令
| 指令 | 作用 | 使用示例 |
|---|
GETFIELD | 获取实例字段值 | mv.visitFieldInsn(GETFIELD, "com/Example", "count", "I") |
PUTFIELD | 设置实例字段值 | mv.visitFieldInsn(PUTFIELD, "com/Example", "count", "I") |
GETSTATIC | 获取静态字段值 | mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;") |
PUTSTATIC | 设置静态字段值 | mv.visitFieldInsn(PUTSTATIC, "com/Example", "instance", "Lcom/Example;") |
4. 类型操作指令
| 指令 | 作用 | 使用示例 |
|---|
NEW | 创建对象 | mv.visitTypeInsn(NEW, "java/util/Date") |
CHECKCAST | 类型转换检查 | mv.visitTypeInsn(CHECKCAST, "java/lang/String") |
INSTANCEOF | 类型检查 | mv.visitTypeInsn(INSTANCEOF, "java/util/List") |
5. 数组操作指令
| 指令 | 作用 | 使用示例 |
|---|
NEWARRAY | 创建基本类型数组 | mv.visitIntInsn(NEWARRAY, T_INT) |
ANEWARRAY | 创建对象数组 | mv.visitTypeInsn(ANEWARRAY, "java/lang/String") |
ARRAYLENGTH | 获取数组长度 | mv.visitInsn(ARRAYLENGTH) |
IALOAD | 加载int数组元素 | mv.visitInsn(IALOAD) |
IASTORE | 存储int数组元素 | mv.visitInsn(IASTORE) |
二、完整操作示例
示例1:添加方法计时逻辑
public static void addTimeLogging(ClassVisitor cv) {
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "process", "()V", null, null);
mv.visitCode();
Label start = new Label();
mv.visitLabel(start);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
mv.visitVarInsn(LSTORE, 1);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
mv.visitVarInsn(LLOAD, 1);
mv.visitInsn(LSUB);
mv.visitVarInsn(LSTORE, 3);
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitVarInsn(LLOAD, 3);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(J)V", false);
Label end = new Label();
mv.visitLabel(end);
mv.visitInsn(RETURN);
mv.visitMaxs(4, 5);
mv.visitEnd();
}
示例2:创建简单类
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS)
cw.visit(Opcodes.V1_8, ACC_PUBLIC, "com/SimpleClass", null, "java/lang/Object", null)
// 添加字段
FieldVisitor fv = cw.visitField(ACC_PRIVATE, "counter", "I", null, null)
fv.visitEnd()
// 添加构造方法
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null)
mv.visitCode()
mv.visitVarInsn(ALOAD, 0)
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false)
mv.visitInsn(RETURN)
mv.visitMaxs(1, 1)
mv.visitEnd()
// 添加getter方法
mv = cw.visitMethod(ACC_PUBLIC, "getCounter", "()I", null, null)
mv.visitCode()
mv.visitVarInsn(ALOAD, 0)
mv.visitFieldInsn(GETFIELD, "com/SimpleClass", "counter", "I")
mv.visitInsn(IRETURN)
mv.visitMaxs(1, 1)
mv.visitEnd()
// 添加setter方法
mv = cw.visitMethod(ACC_PUBLIC, "setCounter", "(I)V", null, null)
mv.visitCode()
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ILOAD, 1)
mv.visitFieldInsn(PUTFIELD, "com/SimpleClass", "counter", "I")
mv.visitInsn(RETURN)
mv.visitMaxs(2, 2)
mv.visitEnd()
cw.visitEnd()
byte[] bytes = cw.toByteArray()
三、最佳实践与技巧
1. 局部变量索引分配
- 非静态方法:索引0 = this
- 方法参数:从1开始顺序分配
- 新增局部变量:从最高参数索引+1开始
- long/double占两个连续索引
2. 类型描述符速查
| Java类型 | 描述符 |
|---|
| int | I |
| long | J |
| float | F |
| double | D |
| boolean | Z |
| String | Ljava/lang/String; |
| int[] | [I |
| Object[][] | [[Ljava/lang/Object; |
3. 调试技巧
ClassReader cr = new ClassReader(bytes);
cr.accept(new TraceClassVisitor(new PrintWriter(System.out)), 0);
java -classpath "asm.jar;asm-util.jar" org.objectweb.asm.util.ASMifier com/YourClass
四、ASM核心组件关系

五、工作流程
