java字节码框架ASM的深入学习
一、什么是asm
asm是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。asm 可以直接产生二进制 class 文件,也可以在类被加载入 java 虚拟机之前动态改变类行为。java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 java 字节码(指令)。asm从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
使用asm框架需要导入asm的jar包,下载链接:。
二、如何使用asm
asm框架中的核心类有以下几个:
① classreader:该类用来解析编译过的class字节码文件。
② classwriter:该类用来重新构建编译后的类,比如说修改类名、属性以及方法,甚至可以生成新的类的字节码文件。
③ classadapter:该类也实现了classvisitor接口,它将对它的方法调用委托给另一个classvisitor对象。
示例1.通过asm生成类的字节码
package com.asm3; import java.io.file; import java.io.filenotfoundexception; import java.io.fileoutputstream; import java.io.ioexception; import org.objectweb.asm.classwriter; import org.objectweb.asm.opcodes; /** * 通过asm生成类的字节码 * @author administrator * */ public class generatorclass { public static void main(string[] args) throws ioexception { //生成一个类只需要classwriter组件即可 classwriter cw = new classwriter(0); //通过visit方法确定类的头部信息 cw.visit(opcodes.v1_5, opcodes.acc_public+opcodes.acc_abstract+opcodes.acc_interface, "com/asm3/comparable", null, "java/lang/object", new string[]{"com/asm3/mesurable"}); //定义类的属性 cw.visitfield(opcodes.acc_public+opcodes.acc_final+opcodes.acc_static, "less", "i", null, new integer(-1)).visitend(); cw.visitfield(opcodes.acc_public+opcodes.acc_final+opcodes.acc_static, "equal", "i", null, new integer(0)).visitend(); cw.visitfield(opcodes.acc_public+opcodes.acc_final+opcodes.acc_static, "greater", "i", null, new integer(1)).visitend(); //定义类的方法 cw.visitmethod(opcodes.acc_public+opcodes.acc_abstract, "compareto", "(ljava/lang/object;)i", null, null).visitend(); cw.visitend(); //使cw类已经完成 //将cw转换成字节数组写到文件里面去 byte[] data = cw.tobytearray(); file file = new file("d://comparable.class"); fileoutputstream fout = new fileoutputstream(file); fout.write(data); fout.close(); } }
生成一个类的字节码文件只需要用到classwriter类即可,生成comparable.class
后用javap指令对其进行反编译:javap -c comparable.class >test.txt
,编译后的结果如下:
public interface com.asm3.comparable extends com.asm3.mesurable { public static final int less; public static final int equal; public static final int greater; public abstract int compareto(java.lang.object); }
注:一个编译后的java类不包含package和import段,因此在class文件中所有的类型都使用的是全路径。
示例2.修改类的字节码文件
c.java
package com.asm5; public class c { public void m() throws interruptedexception{ thread.sleep(100); } }
将c.java类的内容改为如下:
package com.asm5; public class c { public static long timer; public void m() throws interruptedexception{ timer -= system.currenttimemillis(); thread.sleep(100); timer += system.currenttimemillis(); } }
为了弄清楚asm是如何实现的,我们先编译这两个类,然后比对它们的traceclassvisitor的输出,我们可以发现如下的不同(粗体表示)
getstatic c.timer : j
invokestatic java/lang/system.currenttimilis()j
lsub
putstatic c.timer : j
ldc 100
invokestatic java/lang/thread.sleep(j)v
getstatic c.timer : j
invokestatic java/lang/system.currenttimilis()j
ladd
putstatic c.timer : j
return
maxstack=4
maxlocals=1
通过比对上面的指令,我们可以发现必须在m()方法的最前面增加四条指令,在return指令前也增加四条指令,同时这四条必须位于xreturn和athrow之前,因为这些指令都会结束方法的执行。
具体代码如下:
addtimeclassadapter.java
package com.asm5; import org.objectweb.asm.classadapter; import org.objectweb.asm.classvisitor; import org.objectweb.asm.fieldvisitor; import org.objectweb.asm.methodadapter; import org.objectweb.asm.methodvisitor; import org.objectweb.asm.opcodes; public class addtimeclassadapter extends classadapter { private string owner; private boolean isinterface; public addtimeclassadapter(classvisitor cv) { super(cv); } @override public void visit(int version, int access, string name, string signature, string supername, string[] interfaces) { cv.visit(version, access, name, signature, supername, interfaces); owner = name; isinterface = (access & opcodes.acc_interface) != 0; } @override public methodvisitor visitmethod(int access, string name, string desc, string signature, string[] exceptions) { methodvisitor mv = cv.visitmethod(access, name, desc, signature, exceptions); if(!name.equals("<init>") && !isinterface && mv!=null){ //为方法添加计时功能 mv = new addtimemethodadapter(mv); } return mv; } @override public void visitend() { //添加字段 if(!isinterface){ fieldvisitor fv = cv.visitfield(opcodes.acc_public+opcodes.acc_static, "timer", "j", null, null); if(fv!=null){ fv.visitend(); } } cv.visitend(); } class addtimemethodadapter extends methodadapter{ public addtimemethodadapter(methodvisitor mv) { super(mv); } @override public void visitcode() { mv.visitcode(); mv.visitfieldinsn(opcodes.getstatic, owner, "timer", "j"); mv.visitmethodinsn(opcodes.invokestatic, "java/lang/system", "currenttimemillis", "()j"); mv.visitinsn(opcodes.lsub); mv.visitfieldinsn(opcodes.putstatic, owner, "timer", "j"); } @override public void visitinsn(int opcode) { if((opcode>=opcodes.ireturn && opcode<=opcodes.return) || opcode==opcodes.athrow){ mv.visitfieldinsn(opcodes.getstatic, owner, "timer", "j"); mv.visitmethodinsn(opcodes.invokestatic, "java/lang/system", "currenttimemillis", "()j"); mv.visitinsn(opcodes.ladd); mv.visitfieldinsn(opcodes.putstatic, owner, "timer", "j"); } mv.visitinsn(opcode); } @override public void visitmaxs(int maxstack, int maxlocal) { mv.visitmaxs(maxstack+4, maxlocal); } } }
generator.java
package com.asm5; import java.io.file; import java.io.filenotfoundexception; import java.io.fileoutputstream; import java.io.ioexception; import org.objectweb.asm.classadapter; import org.objectweb.asm.classreader; import org.objectweb.asm.classwriter; public class generator { public static void main(string[] args){ try { classreader cr = new classreader("com/asm5/c"); classwriter cw = new classwriter(classwriter.compute_maxs); classadapter classadapter = new addtimeclassadapter(cw); //使给定的访问者访问java类的classreader cr.accept(classadapter, classreader.skip_debug); byte[] data = cw.tobytearray(); file file = new file(system.getproperty("user.dir") + "\\webroot\\web-inf\\classes\\com\\asm5\\c.class"); fileoutputstream fout = new fileoutputstream(file); fout.write(data); fout.close(); system.out.println("success!"); } catch (filenotfoundexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } } }
下面是一个测试类:
package com.asm5; public class test { public static void main(string[] args) throws interruptedexception, nosuchfieldexception, securityexception, illegalargumentexception, illegalaccessexception { c c = new c(); c.m(); class cc = c.getclass(); system.out.println(cc.getfield("timer").get(c)); } }
输出结果为:100
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。
上一篇: php获取今日开始时间和结束时间的方法
下一篇: 指针常量和常量指针