ASM 是一个 Java 字节码操纵框架。它可以直接以二进制形式动态地生成 sub类或其他代理类,或者在装载时动态地修改类。ASM 提供类似于 BCEL 和 SERP 之类的工具包的功能,但是被设计得更小巧、更快速,这使它适用于实时代码插装。
asmsupport是在asm的基础上做的一封装,使用asmsupport将最大限度的降低字节码操作的成本,屏蔽了使用asm的时候对堆栈操作,以及原生的字节码指令。同时增加了更多的灵活性,采用匿名类的方式是方法块更清晰。
这里我们简单的使用asm和asmsupport来生成如下代码:
public class Test { public static void main(String name) { System.out.println("Hello : " + name); } }
1. ClassWriter mv = cw.visitMethod(Opcodes.ACCpublic_ + Opcodes.ACC_STATIC, "main", "(Ljava/lang/String;)V", null, null); 2. mv.visitCode(); 3. Label l0 = new Label(); 4. mv.visitLabel(l0); 5. mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 6. mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); 7. mv.visitInsn(DUP); 8. mv.visitLdcInsn("Hello : "); 9. mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", false); 10. mv.visitVarInsn(ALOAD, 0); 11. mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); 12. mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false); 13. mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); 14. Label l1 = new Label(); 15. mv.visitLabel(l1); 16. mv.visitInsn(RETURN); 17. Label l2 = new Label(); 18. mv.visitLabel(l2); 19. mv.visitLocVar("name", "Ljava/lang/String;", null, l0, l2, 0); 20. mv.visitMaxs(4, 1); 21. mv.visitEnd(); 22. cw.visitEnd(); 23. byte[] classBytes = cw.toByteArray(); 24. //convert class Bytes to class
从上面可以看到我们想要创建一个class不仅需要使用复杂的字节码指令(比如第5,6,9…行),而且还要关注堆栈操作(比如第10行就是从局部变量中获取第0个位置,就是方法的参数name),同时在最后需要确定好方法所需要的堆栈数目(第20行)。对一些隐式的方法字节码还需特殊处理,比如第6~12行的代码对应的就是(“Hello : ” + name),但实际根据字节码生成的内容实际是调用了new StringBuilder(“Hello : ”).append(name).toString().这样就大大的增加了开发难度。
DummyClass dc = new DummyClass().public_().name("TestCommon").setClassOutPutPath(".//target//dummy-generated"); dc.newMethod("main").public_().static_() .argTypes(String.class).argNames_("name") .body_(new MethodBody(){ @Override public void body(LocVar... args) { call_(defType(System.class).field("out"), "println", stradd(val("Hello : "), args[0])); return_(); } } ); Class cls = dc.build();
使用asmsupport完全屏蔽了字节码指令和堆栈操作,更重要的一点是采用匿名类的方式,是的程序的结构更清晰,倾向与原始的java代码结构,使用简单更灵活。