一、什么是ASM
ASM是一个JAVA字节码分析、创建和修改的开源应用框架。在ASM中提供了诸多的API用于对类的内容进行字节码操作的方法。与传统的BCEL和SERL不同,在ASM中提供了更为优雅和灵活的操作字节码的方式。目前ASM已被广泛的开源应用架构所使用,例如:Spring、Hibernate等。
二、ASM能干什么
分析一个类、从字节码角度创建一个类、修改一个已经被编译过的类文件
三、ASM初探例子
这里我们使用ASM的CoreAPI(ASM提供了两组API:Core和Tree,Core是基于访问者模式来操作类的,而Tree是基于树节点来操作类的)创建一个MyClass类,目标类如下:
public class MyClass { private String name; public Myclass(){ this.name = "zhangzhuo"; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
这个类在构造方法中初始化了属性name,并提供了两个public方法来修改和访问name属性。
接下来就要书写创建这个类的代码了,现将代码给出,然后逐步解释,代码如下:
代码1:
public class GenerateClass { public void generateClass() { //方法的栈长度和本地变量表长度用户自己计算 ClassWriter classWriter = new ClassWriter(0); //Opcodes.V1_6指定类的版本 //Opcodes.ACC_PUBLIC表示这个类是public, //“org/victorzhzh/core/classes/MyClass”类的全限定名称 //第一个null位置变量定义的是泛型签名, //“java/lang/Object”这个类的父类 //第二个null位子的变量定义的是这个类实现的接口 classWriter.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, "org/victorzhzh/core/classes/MyClass", null, "java/lang/Object", null); ClassAdapter classAdapter = new MyClassAdapter(classWriter); classAdapter.visitField(Opcodes.ACC_PRIVATE, "name", Type.getDescriptor(String.class), null, null);//定义name属性 classAdapter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null).visitCode();//定义构造方法 String setMethodDesc = "(" + Type.getDescriptor(String.class) + ")V"; classAdapter.visitMethod(Opcodes.ACC_PUBLIC, "setName", setMethodDesc, null, null).visitCode();//定义setName方法 String getMethodDesc = "()" + Type.getDescriptor(String.class); classAdapter.visitMethod(Opcodes.ACC_PUBLIC, "getName", getMethodDesc, null, null).visitCode();//定义getName方法 byte[] classFile = classWriter.toByteArray();//生成字节码 MyClassLoader classLoader = new MyClassLoader();//定义一个类加载器 Class clazz = classLoader.defineClassFromClassFile( "org.victorzhzh.core.classes.MyClass", classFile); try {//利用反射方式,访问getName Object obj = clazz.newInstance(); Method method = clazz.getMethod("getName"); System.out.println(obj.toString()); System.out.println(method.invoke(obj, null)); } catch (Exception e) { e.printStackTrace(); } } class MyClassLoader extends ClassLoader { public Class defineClassFromClassFile(String className, byte[] classFile) throws ClassFormatError { return defineClass(className, classFile, 0, classFile.length); } } public static void main(String[] args) { GenerateClass generateClass = new GenerateClass(); generateClass.generateClass(); } }
代码2:
public class MyClassAdapter extends ClassAdapter { public MyClassAdapter(ClassVisitor cv) { super(cv); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor methodVisitor = cv.visitMethod(access, name, desc, signature, exceptions); if (name.equals("<init>")) { return new InitMethodAdapter(methodVisitor); } else if (name.equals("setName")) { return new SetMethodAdapter(methodVisitor); } else if (name.equals("getName")) { return new GetMethodAdapter(methodVisitor); } else { return super.visitMethod(access, name, desc, signature, exceptions); } } //这个类生成具体的构造方法字节码 class InitMethodAdapter extends MethodAdapter { public InitMethodAdapter(MethodVisitor mv) { super(mv); } @Override public void visitCode() { mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");//调用父类的构造方法 mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitLdcInsn("zhangzhuo");//将常量池中的字符串常量加载刀栈顶 mv.visitFieldInsn(Opcodes.PUTFIELD, "org/victorzhzh/core/classes/MyClass", "name", Type.getDescriptor(String.class));//对name属性赋值 mv.visitInsn(Opcodes.RETURN);//设置返回值 mv.visitMaxs(2, 1);//设置方法的栈和本地变量表的大小 } }; //这个类生成具体的setName方法字节码 class SetMethodAdapter extends MethodAdapter { public SetMethodAdapter(MethodVisitor mv) { super(mv); } @Override public void visitCode() { mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ALOAD, 1); mv.visitFieldInsn(Opcodes.PUTFIELD, "org/victorzhzh/core/classes/MyClass", "name", Type.getDescriptor(String.class)); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(2, 2); } } //这个类生成具体的getName方法字节 class GetMethodAdapter extends MethodAdapter { public GetMethodAdapter(MethodVisitor mv) { super(mv); } @Override public void visitCode() { mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitFieldInsn(Opcodes.GETFIELD, "org/victorzhzh/core/classes/MyClass", "name", Type.getDescriptor(String.class));//获取name属性的值 mv.visitInsn(Opcodes.ARETURN);//返回一个引用,这里是String的引用即name mv.visitMaxs(1, 1); } } }
运行结果:
org.victorzhzh.core.classes.MyClass@1270b73 zhangzhuo
这个例子只是简单地介绍了一下ASM如何创建一个类,接下来的几个章节,将详细介绍ASM的CoreAPI和TreeAPI中如何操作类。