ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

二、如何使用ASM

  ASM框架中的核心类有以下几个:

  ①  ClassReader:该类用来解析编译过的class字节码文件。

  ②  ClassWriter:该类用来重新构建编译后的类,比如说修改类名、属性以及方法,甚至可以生成新的类的字节码文件。

  ③  ClassAdapter:该类也实现了ClassVisitor接口,它将对它的方法调用委托给另一个ClassVisitor对象。

示例1:通过ClassWriter生成类的字节码

package samyang.asm.example1;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Opcodes;

/**
 * @Description: 通过ASM生成类的字节码
 */
public class GeneratorClass {

    public static void main(String[] args) throws IOException {
        //生成一个类只需要ClassWriter组件即可
        ClassWriter cw = new ClassWriter(0);
        //通过visit方法确定类的头部信息
        cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT + Opcodes.ACC_INTERFACE,
            "samyang/asm/example1/Comparable", null, "java/lang/Object",
            new String[] {"samyang/asm/example1/Mesurable"});
        // 定义类的属性
        cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,
            "LESS", "I", null, new Integer(-1)).visitEnd();
        cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,
            "EQUAL", "I", null, new Integer(0)).visitEnd();
        cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_STATIC,
            "GREATER", "I", null, new Integer(1)).visitEnd();
        // 定义类的方法
        cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT, "compareTo",
            "(Ljava/lang/Object;)I", null, null).visitEnd();
        //使cw类已经完成
        cw.visitEnd();
        //将cw转换成字节数组写到文件里面去
        byte[] data = cw.toByteArray();
        File file = new File("target\\classes\\samyang\\asm\\example1\\Generator.class");
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(data);
        fos.close();
    }
}

生成一个类的字节码文件只需要用到ClassWriter类即可,生成Comparable.class后用javap指令对其进行反编译:javap -c Comparable.class >test.txt。

注:一个编译后的java类不包含package和import段,因此在class文件中所有的类型都使用的是全路径。

ClassWriter类中方法说明:

(1)定义类头部信息


public final void visit(int version, int access, String name, String signature, String superName, String[] interfaces)


version:类版本

access:类的访问标识

name:类名称

signature:类签名,如果类不是通用类,并且不扩展或实现泛型类或接口,则可能为null。

superName:超类名称,如果是接口或超类为Object则可能为null

interfaces:类实现的接口名称列表

(2)定义类属性


public final FieldVisitor visitField(int access, String name, String desc, String signature, Object value)


access:字段访问标识

name:字段名称

desc:字段描述

signature:字段签名,若字段类型不是泛型则可以为null

value:字段初始值

(3)定义类方法


public final MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)


access:方法访问标识

name:方法名称

desc:方法描述

signature:方法签名,若方法参数、返回类型和异常没有使用泛型则可能为null

exceptions:方法的异常名,可能为null