代码动态生成利器ASM

小巧而神奇的ASM

ASM是一套JAVA字节码生成架构。它可以动态生成二进制格式的stub类或其他代理类,或者在类被JAVA虚拟机装入内存之前,动态修改类。 ASM 提供了与 BCEL( http://jakarta.apache.org/bcel )和SERP( http://serp.sourceforge.net/ )相似的功能,只有22K的大小,比起350K的BCEL和150K的SERP来说,是相当小巧的,并且它有更高的执行效率,是BCEL的7倍,SERP 的11倍以上。ASM一贯的设计思想就是将其应用于动态生成领域,因此小巧和快捷一直是这个产品的设计和实现的指导思想。

此产品由法国电信公司的研发工程师Eric Bruneton负责。从2002年7月ASM的第一个版本发布至今,此产品已经升级了五次,日臻完美。到目前为止,ASM最新的版本是1.3.5,你可以去 http://asm.objectweb.org/ 下载。

ASM的最终目标是创建一个生成工具,可以被用来执行对任何类的处理操作(不像一些工具,比如Javassit,它只支持预先定义的类操作,然而在许多场合这一功能是有局限性的)。

JAVA的CLASS文件格式

要想驾驭ASM,先要了解一下JAVA的CLASS文件格式。JAVA的CLASS文件通常是树型结构。根节点包含以下元素:

  • ConstantPool:符号表;
  • FieldInfo:类中的成员变量信息;
  • MethodInfo:类中的方法描述;
  • Attribute:可选的附加节点。

FieldInfo节点包含成员变量的名称,诸如public,private,static等的标志。ConstantValue属性用来存储静态的不变的成员变量的值。Deprecated和Synthetic被用来标记一个成员变量是不被推荐的或由编译器生成的。

MethodInfo节点包含方法的名称,参数的类型和和它的返回值,方法是公有的,私有的或静态的等标志。MethodInfo包含可选的附加属 性,其中最重要的是Code属性,它包含非抽象的方法的代码。Exceptions属性包含方法将抛出的Exception的名称。Deprecated 和Synthetic属性的信息同上面的FieldInfo的定义一样。

根节点的可选属性有SourceFile,InnerClasses和Deprecated。SourceFile用来存储被编译成字节码的源代码 文件的原始名称;InnerClasses存储内部类的信息。由于这些属性的存在,java 的类格式是可以扩展的,也就是说可以在一个class中附加一些非标准的属性, java虚拟机会忽略这些不可识别的属性,正常的加载这个class。

ConstantPool是一个由数字或字符串常量的索引组成的队列,或由此类的树的其他节点引用的,由其他对象创建的被引用常量的索引组成的队 列。这个表的目标是为了减少冗余。例如,FieldInfo节点不包含节点的名称,只包含它在这一表中的索引。同样的,GETFIELD和 PUTFIELD不直接包含成员变量的名称,只包含名称的索引。

精通ASM

Asm架构整体都围绕着两个接口,即ClassVisitor 和 CodeVisitor,它们能访问每个类的方法,成员变量,包含在每个方法中的字节码指令。ClassReader用来读取class文件;ClassWriter类用来写生成的Class文件。

为了修改已经存在的class,你必须使用分析class文件的ClassReader,类的修正器和写class文件的ClassWriter。 类的修正器就是一个ClassVisitor,它可以委派一部分工作到其他的ClassVisitor,但是为了实现预期的修改步骤,它将改变一些参数的 值,或者调用一些其他方法。为了比较容易的实现这种类的修正器,ASM提供了一个ClassAdapter和CodeAdapter,这两个适配器类分别 实现了ClassVistor和CodeVistor接口。

HelloWorld,体验造类的神奇

下面是一个应用ASM动态生成字节码的类,并调用其中方法的完整的HelloWorld 程序,程序的功能是动态生成一个Example.class类,并实例化一个Example对象,调用对象的main函数,在屏幕上打印出"Hello world!"

import org.objectweb.asm.*;
import java.lang.reflect.*;
import java.io.FileOutputStream;

public class Helloworld extends ClassLoader implements Constants {

       public static void main (final String args[]) throws Exception {
       /* * 此程序将生成一个class,对应的java源代码是:
         * * public class Example {
         *           public static void main (String[] args) {
         *               System.out.println("Hello world!");
         *           }
         *     }
        * */
         // 创建一个ClassWriter
         ClassWriter cw = new ClassWriter(false);
         cw.visit(ACC_PUBLIC, "Example", "java/lang/Object", null, null);
         // 创建一个 MethodWriter
         CodeVisitor mw = cw.visitMethod(ACC_PUBLIC, "", "()V", null);
         // 推入 磘his 变量
         mw.visitVarInsn(ALOAD, 0);
         // 创建父类的构造函数
         mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V");                mw.visitInsn( ETURN);
         // 这段代码使用最多一个栈元素和一个本地变量
         mw.visitMaxs(1, 1);
         // 为main方法创建一个 
         MethodWriter mw = cw.visitMethod( ACC_PUBLIC ACC_STATIC,                                                                          "main","([Ljava/lang/String;)V", null);
         // 使用System类的out成员类
         mw.visitFieldInsn( GETSTATIC, "java/lang/System", "out",                        "Ljava/io/PrintStream;");
         // pushes the "Hello World!"
         String constant mw.visitLdcInsn("Hello world!");
         // 调用System.out的磒rintln 函数
         mw.visitMethodInsn( INVOKEVIRTUAL,
                                            "java/io/PrintStream",
                                             "println",
                                               "(Ljava/lang/String;)V");
         mw.visitInsn(RETURN);
         // 这段代码使用最多两个栈元素和两个本地变量
         mw.visitMaxs(2, 2);
         // 生成字节码形式的类
         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);
       // 使用动态生成的类打印 碒elloworld
       Method main = exampleClass.getMethods()[0];
       main.invoke(null, new Object[] {null});
}


转载:http://blog.sina.com.cn/s/blog_4b38e200010008tn.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值