使用ASM框架生成Java class文件
更新日期:
ASM字节码操作框架,是Java中用于字节码操作的很好用的一个库,是cglib等很多著名框架的基础,我们常用的Spring,Hibernate等都用到了cglib。ASM存在的基础是,JVM虚拟机未对Java的字节码(.class)文件做“只能是文件”的限制,只要是合法的字节码文件格式,可以存在与网络上,内存中,都可以予以加载并运行。
本文使用ASM框架手动生成一段已有的Java代码,并予以加载和运行。ASM框架的代码可以去这里下载,用户手册可以去这里下载,官网是这里,官网上有一些文章和教程,可以用作入门。
生成字节码的源代码是这样,
使用ASM框架,生成上述代码的字节码的代码如下,
以上代码做如下解释, 1. 首先使用ClassWriter
创建一个写入class字节码的对象,用cw.visit
创建一个类my.Example
。 2. 然后用cw.visitMethod
获取到类方法的MethodVisitor
对象,使用该对象新增类中的方法。 3. 先新增一个无参的构造方法,对应上述代码“add constructor”一段,方法的描述符是()V
,表示无参数,返回是void
,这个与Java字节码文件规范保持一致。 4. 然后类似的构造main
方法,在main
方法中,先使用mw.visitVarInsn
创建两个临时变量,并用mw.visitIntInsn
赋初始值6和7。 5. 接下来,用mw.visitInsn
完成加法和乘法,并将值赋给一个新的临时变量。 6. 在main
方法的结尾,调用mw.visitFieldInsn
获取静态的PrintStream
对象,调用mw.visitMethodInsn
来完成println
方法的调用,并结束方法mw.visitEnd
。 7. 以上步骤已经完成了字节码的生成,调用cw.toByteArray
将其保存到内存中的一个byte[]
对象里,然后调用ClassLoader
对象的loadClass
方法,加载这个类,注意,这里要重写findClass
,在其中指明使用byte[]
对象里的内容作为字节码来加载。 8. 如果需要将字节码输出可以使用FileOutputStream
的write
方法,这里已经注释掉,这段代码对调试很有用,可以用class文件反编译工具来查看是否是想要的代码。 9. 最后,使用Java的反射机制,分别调用getMethod
和invoke
来调用之前加载的class的main
方法,输出39
。
注意,局部变量并不可以指定名称,只可以指定在操作数栈中的位置,因此变量名i,j,k
并未出现在以上代码中。可以看到,基本上,ASM是对Java字节码指令的一个比较好用的封装,与写汇编代码有以一些相似之处。如果需要生成的代码有static
域,例如这样,
同样可以使用ASM生成这段字节码,只是局部代码需要做一些改动,修改后的代码如下,
多数的代码和之前的有重复,不同之处如下, 1. 使用cw.visitField
,新增静态对象a和b。 2. 使用mw.visitFieldInsn
将静态对象压入操作数栈。