简介:ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
Asm架构整体都围绕着两个接口,即ClassVisitor 和 CodeVisitor,它们能访问每个类的方法,成员变量,包含在每个方法中的字节码指令。ClassReader用来读取class文件;ClassWriter类用来写生成的Class文件。
为了修改已经存在的class,你必须使用分析class文件的ClassReader,类的修正器和写class文件的ClassWriter。 类的修正器就是一个ClassVisitor,它可以委派一部分工作到其他的ClassVisitor,但是为了实现预期的修改步骤,它将改变一些参数的 值,或者调用一些其他方法。为了比较容易的实现这种类的修正器,ASM提供了一个ClassAdapter和CodeAdapter,这两个适配器类分别 实现了ClassVistor和CodeVistor接口。
ASM官网:http://asm.ow2.org/
ASM最新版本是4.0 下载地址:http://forge.ow2.org/projects/asm/
学习ASM最好能下载ASM的一个eclipse的插件,这个插件能看你当前类如果用ASM来生成代码应该怎样写。插件下载地址:http://download.forge.objectweb.org/eclipse-update/
下面用ASM写一个HelloWorld,体验一下哈。
import java.io.FileOutputStream; import java.lang.reflect.Method;
import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes;
public class HelloWorld extends ClassLoader {
/** * 如何使用ASM动态生成一个类,并打印出HelloWorld! * * @param args * @throws Exception */ public static void main(String[] args) throws Exception { // 创建一个ClassWriter来写示例类,这个类继承Object ClassWriter cw = new ClassWriter(0); /* * 第一个参数:JDK版本 * 第二个参数:这个类的访问标记 * 第三个参数:这个类的名字 * 第四个参数:这个类的签名,当这个类没有继承或者实现一个接口的时候可以为空。 * 第五个参数:当前类父类的名字 接口的父类是Object当该类是Object的时候为空 * 第六个参数:接口的名字 可以为空 */ cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, "Example", null, "java/lang/Object", null); /* * 创建一个写默认构造器的MethodWriter * 第一个参数:方法的访问标记 * 第二个参数:方法名称 * 第三个参数:方法的描述符号 * 第四个参数:方法签名(当方法参数 返回类型 以及异常没有用到类属性的时候可以为空) * 第⑤个参数:异常名称 */ MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null); // 压入this变量 mv.visitVarInsn(Opcodes.ALOAD, 0); // 执行父类构造器 mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); mv.visitInsn(Opcodes.RETURN); // 这段代码使用最多一个堆元素 和一个 局部变量 mv.visitMaxs(1, 1); mv.visitEnd(); // 创建一个main方法的MethodWriter mv = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); // 调用System类的PrintStream类的out mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); // 压入"Hello World!" 常量 mv.visitLdcInsn("Hello World!"); // 执行定义在PrintStream中的println方法 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); mv.visitInsn(Opcodes.RETURN); // 使用两个堆和两个局部变量 mv.visitMaxs(2, 2); // 获取Example类的字节码并且动态加载它。 byte[] code = cw.toByteArray(); FileOutputStream fos = new FileOutputStream("Example.class"); fos.write(code); fos.close();
HelloWorld loader = new HelloWorld(); Class<?> exampleClass = loader.defineClass("Example", code, 0, code.length); // 使用动态生成的类打印HelloWorld Method method = exampleClass.getMethods()[0]; method.invoke(null, new Object[] { null }); } }