[code]
想通过asm的代码生成来写.class文件至少得了解下面的东西。
1.ICONST_x相当于常量,前面的I是指int型,还有其它型的CONST,这个量为比如ICONST_1就是1,对于后面不同的操作,它会再指定实际的类型。使用visitInsn(ICONST_1)就是说定义了一个为1的临时量压到栈里面,但不会定义一个变量。-1<=ICONST<=5;如果要定义大点的数就使用visitVarInsn(BIPUSH, 10)
2.ISTORE是把栈顶的临时量保存成变量如visitVarInsn(ISTORE, 1)保存在位置1,如果想把变量值拿出来用如下进行,把位置1的值拿出来放到栈顶visitVarInsn(ILOAD, 1);
3.visitIincInsn(1, 1);对位置是1的变量进行自增,自增幅度为后面参数所定义的1。
4.对于栈的操作还有DUP,POP,SWAP等命令。需要理解栈的存取。
5.for循环例子代码,使用了两个label来完成,开始初学对字节码不明白,所以先从.class文件反译成字节码来进行。相当于实现:
-----------------------------
for(int i = 1; i >= 10;) {
i++;
byte byte0 = 2;
}
-----------------------------
Label forLabel = new Label();
Label endLabel = new Label();
mw.visitInsn(ICONST_1);
mw.visitVarInsn(ISTORE, 1);
mw.visitLabel(forLabel);
mw.visitVarInsn(ILOAD, 1);
mw.visitVarInsn(BIPUSH, 10);
mw.visitJumpInsn(IF_ICMPLT, endLabel);
mw.visitIincInsn(1, 1);// ++操作
mw.visitInsn(ICONST_2);
mw.visitVarInsn(ISTORE, 2); //这循环体内进做了 int c = 2的操作
mw.visitJumpInsn(GOTO, forLabel);
mw.visitLabel(endLabel);
5.条件语句例子:没用使用GOTO,相当于实现
-------------------------
int i = 3;
if(i >= 0)
i = 1 + i;
--------------------------
Label label = new Label();
mw.visitInsn(ICONST_3);
mw.visitVarInsn(ISTORE, 1);
mw.visitVarInsn(ILOAD, 1);
mw.visitJumpInsn(IFLT, label);
mw.visitInsn(ICONST_1);
mw.visitIntInsn(ILOAD, 1);
mw.visitInsn(IADD);
mw.visitVarInsn(ISTORE, 1);
mw.visitLabel(label);
--------------------------------------------------
Visitor访问者模式作用于某个对象的操作,它可以使你在不改变这些对象本身的情况下,定义作用于这些对象的新操作。
在asm中,ClassReader对象可以接受一个实现ClassVisitor接口的对象来进行对读入的Class的访问与控制。
其中,如asm文档展示了一个很简单的ClassPrinter类,用来打印类相关信息。
ClassPrinter cp = new ClassPrinter();
ClassReader cr = new ClassReader("java.lang.Runnable");
cr.accept(cp, 0);
ClassPrinter实现了ClassVisitor接口
public class ClassPrinter implements ClassVisitor {
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
System.out.println(name + " extends " + superName + " {");
}
public void visitSource(String source, String debug) {
System.out.println("[" + source + "]");
}
public void visitOuterClass(String owner, String name, String desc) {
System.out.println("[outerClass]" + owner + " " + name + " " + desc);
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return null;
}
public void visitAttribute(Attribute attr) {
System.out.println("[Attribute]" + attr);
}
public void visitInnerClass(String name, String outerName,
String innerName, int access) {
}
public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value) {
System.out.println(" " + desc + " " + name);
return null;
}
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
System.out.println(" " + name + desc);
return null;
}
public void visitEnd() {
System.out.println("}");
}
}
对于ClassVisitor接口,asm在其之上提供了一个适配器的功能。ClassAdapter用来对ClassReader和ClassWriter中间的适配工作。ClassAdapter实现了ClassVisitor接口,是一个具体类。我们可以继承于ClassAdapter类来实现我们的逻辑。
ClassAdatper作为具体类的好处是我们只需要重写我们关心的东西。使用ClassAdapter我们可以在字节码级别更改我们的类,一个应用就是实现我们的AOP。
这是我们的目标类
package com.c2.asm;
public class B {
public int number = 20;
public int getNumber() {
System.out.println("class B logic");
return number;
}
public static void main(String[] args) {
B b = new B();
System.out.println(b.getNumber());
}
}
我们的任务是对getNumber()方法的更改,在打印"class B logic"之前实现使number的值由原来的20增加到23,在打印"class B logic"之后实现打印"after"语句。
设计类AopExampleAdapter,当中使用到了AopMethodAdapter来实施主要改写逻辑。
class AopMethodAdapter extends MethodAdapter implements Opcodes {
public AopMethodAdapter(MethodVisitor mv) {
super(mv);
}
// 执行方法之前
@Override
public void visitCode() {
mv.visitVarInsn(ALOAD, 0); //将第一个参数带入方法,并将其推送至执行堆栈
mv.visitInsn(DUP);
mv.visitFieldInsn(GETFIELD, "com/c2/asm/B", "number", "I");
mv.visitInsn(ICONST_3);
mv.visitInsn(IADD);
mv.visitFieldInsn(PUTFIELD, "com/c2/asm/B", "number", "I");
}
//执行方法之后
@Override
public void visitInsn(int opcode) {
if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out",
"Ljava/io/PrintStream;");
mv.visitLdcInsn("after");
// invokes the 'println' method (defined in the PrintStream class)
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println",
"(Ljava/lang/String;)V");
}
mv.visitInsn(opcode);
}
//修改类中使用到的stack和locals大小,我没仔细算过 +2,+3都瞎写的
@Override
public void visitMaxs(int maxStack, int maxLocals) {
mv.visitMaxs(maxStack + 2, maxLocals + 3);
}
}
public class AopExampleAdapter extends ClassAdapter implements Opcodes {
public AopExampleAdapter(ClassVisitor cv) {
super(cv);
}
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor mv;
mv = cv.visitMethod(access, name, desc, signature, exceptions);
// 当匹配到getNumber方法后进行对方法修改
if (mv != null && name.equals("getNumber")) {
mv = new AopMethodAdapter(mv);
}
return mv;
}
}
使用AopMethodAdapter如ClassVisitor方法一样,因为AopMethodAdapter是基于ClassVisitor来实现的。
“aload_0”指示符将第一个参数带入方法,并将其推送至执行堆栈。“invokespecial”指示符,不言而喻,它将调用实例的方法,但是忽略传统的动态绑定
[/code]
想通过asm的代码生成来写.class文件至少得了解下面的东西。
1.ICONST_x相当于常量,前面的I是指int型,还有其它型的CONST,这个量为比如ICONST_1就是1,对于后面不同的操作,它会再指定实际的类型。使用visitInsn(ICONST_1)就是说定义了一个为1的临时量压到栈里面,但不会定义一个变量。-1<=ICONST<=5;如果要定义大点的数就使用visitVarInsn(BIPUSH, 10)
2.ISTORE是把栈顶的临时量保存成变量如visitVarInsn(ISTORE, 1)保存在位置1,如果想把变量值拿出来用如下进行,把位置1的值拿出来放到栈顶visitVarInsn(ILOAD, 1);
3.visitIincInsn(1, 1);对位置是1的变量进行自增,自增幅度为后面参数所定义的1。
4.对于栈的操作还有DUP,POP,SWAP等命令。需要理解栈的存取。
5.for循环例子代码,使用了两个label来完成,开始初学对字节码不明白,所以先从.class文件反译成字节码来进行。相当于实现:
-----------------------------
for(int i = 1; i >= 10;) {
i++;
byte byte0 = 2;
}
-----------------------------
Label forLabel = new Label();
Label endLabel = new Label();
mw.visitInsn(ICONST_1);
mw.visitVarInsn(ISTORE, 1);
mw.visitLabel(forLabel);
mw.visitVarInsn(ILOAD, 1);
mw.visitVarInsn(BIPUSH, 10);
mw.visitJumpInsn(IF_ICMPLT, endLabel);
mw.visitIincInsn(1, 1);// ++操作
mw.visitInsn(ICONST_2);
mw.visitVarInsn(ISTORE, 2); //这循环体内进做了 int c = 2的操作
mw.visitJumpInsn(GOTO, forLabel);
mw.visitLabel(endLabel);
5.条件语句例子:没用使用GOTO,相当于实现
-------------------------
int i = 3;
if(i >= 0)
i = 1 + i;
--------------------------
Label label = new Label();
mw.visitInsn(ICONST_3);
mw.visitVarInsn(ISTORE, 1);
mw.visitVarInsn(ILOAD, 1);
mw.visitJumpInsn(IFLT, label);
mw.visitInsn(ICONST_1);
mw.visitIntInsn(ILOAD, 1);
mw.visitInsn(IADD);
mw.visitVarInsn(ISTORE, 1);
mw.visitLabel(label);
--------------------------------------------------
Visitor访问者模式作用于某个对象的操作,它可以使你在不改变这些对象本身的情况下,定义作用于这些对象的新操作。
在asm中,ClassReader对象可以接受一个实现ClassVisitor接口的对象来进行对读入的Class的访问与控制。
其中,如asm文档展示了一个很简单的ClassPrinter类,用来打印类相关信息。
ClassPrinter cp = new ClassPrinter();
ClassReader cr = new ClassReader("java.lang.Runnable");
cr.accept(cp, 0);
ClassPrinter实现了ClassVisitor接口
public class ClassPrinter implements ClassVisitor {
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
System.out.println(name + " extends " + superName + " {");
}
public void visitSource(String source, String debug) {
System.out.println("[" + source + "]");
}
public void visitOuterClass(String owner, String name, String desc) {
System.out.println("[outerClass]" + owner + " " + name + " " + desc);
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return null;
}
public void visitAttribute(Attribute attr) {
System.out.println("[Attribute]" + attr);
}
public void visitInnerClass(String name, String outerName,
String innerName, int access) {
}
public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value) {
System.out.println(" " + desc + " " + name);
return null;
}
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
System.out.println(" " + name + desc);
return null;
}
public void visitEnd() {
System.out.println("}");
}
}
对于ClassVisitor接口,asm在其之上提供了一个适配器的功能。ClassAdapter用来对ClassReader和ClassWriter中间的适配工作。ClassAdapter实现了ClassVisitor接口,是一个具体类。我们可以继承于ClassAdapter类来实现我们的逻辑。
ClassAdatper作为具体类的好处是我们只需要重写我们关心的东西。使用ClassAdapter我们可以在字节码级别更改我们的类,一个应用就是实现我们的AOP。
这是我们的目标类
package com.c2.asm;
public class B {
public int number = 20;
public int getNumber() {
System.out.println("class B logic");
return number;
}
public static void main(String[] args) {
B b = new B();
System.out.println(b.getNumber());
}
}
我们的任务是对getNumber()方法的更改,在打印"class B logic"之前实现使number的值由原来的20增加到23,在打印"class B logic"之后实现打印"after"语句。
设计类AopExampleAdapter,当中使用到了AopMethodAdapter来实施主要改写逻辑。
class AopMethodAdapter extends MethodAdapter implements Opcodes {
public AopMethodAdapter(MethodVisitor mv) {
super(mv);
}
// 执行方法之前
@Override
public void visitCode() {
mv.visitVarInsn(ALOAD, 0); //将第一个参数带入方法,并将其推送至执行堆栈
mv.visitInsn(DUP);
mv.visitFieldInsn(GETFIELD, "com/c2/asm/B", "number", "I");
mv.visitInsn(ICONST_3);
mv.visitInsn(IADD);
mv.visitFieldInsn(PUTFIELD, "com/c2/asm/B", "number", "I");
}
//执行方法之后
@Override
public void visitInsn(int opcode) {
if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out",
"Ljava/io/PrintStream;");
mv.visitLdcInsn("after");
// invokes the 'println' method (defined in the PrintStream class)
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println",
"(Ljava/lang/String;)V");
}
mv.visitInsn(opcode);
}
//修改类中使用到的stack和locals大小,我没仔细算过 +2,+3都瞎写的
@Override
public void visitMaxs(int maxStack, int maxLocals) {
mv.visitMaxs(maxStack + 2, maxLocals + 3);
}
}
public class AopExampleAdapter extends ClassAdapter implements Opcodes {
public AopExampleAdapter(ClassVisitor cv) {
super(cv);
}
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor mv;
mv = cv.visitMethod(access, name, desc, signature, exceptions);
// 当匹配到getNumber方法后进行对方法修改
if (mv != null && name.equals("getNumber")) {
mv = new AopMethodAdapter(mv);
}
return mv;
}
}
使用AopMethodAdapter如ClassVisitor方法一样,因为AopMethodAdapter是基于ClassVisitor来实现的。
“aload_0”指示符将第一个参数带入方法,并将其推送至执行堆栈。“invokespecial”指示符,不言而喻,它将调用实例的方法,但是忽略传统的动态绑定
[/code]