写在前面
本文看下如何通过asm生成变量并sout。
1:代码
直接看代码吧,注释很详细,有不懂的,留言告诉我:
package com.dahuyuo.asmtest;
import org.objectweb.asm.*;
import org.objectweb.asm.commons.AdviceAdapter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import static org.objectweb.asm.Opcodes.ASM5;
public class YY extends ClassLoader {
    public static void main(String[] args) throws Exception {
        ClassReader cr = new ClassReader("com.dahuyuo.asmtest.XX");
        ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
        cr.accept(new ClassVisitor(ASM5, cw) {
            public MethodVisitor visitMethod(int access, String name, String
                    descriptor, String signature, String[] exceptions) {
                // 方法过滤
                if (!"didi".equals(name)) {
                    return super.visitMethod(access, name, descriptor,
                            signature, exceptions);
                }
                MethodVisitor mv = super.visitMethod(access, name, descriptor,
                        signature, exceptions);
                return new AdviceAdapter(ASM5, mv, access, name, descriptor) {
                    protected void onMethodEnter() {
//                        super.onMethodEnter();
                        // 这里不管定义啥类型都可以用Type.LONG_TYPE,没搞懂是干啥的
                        int intVar1 = newLocal(Type.INT_TYPE); // 创建一个int类型的局部变量 相当于int var;
                        mv.visitLdcInsn(99); // 将常量99加载到操作数栈的栈顶
                        mv.visitVarInsn(ISTORE, intVar1); // 将栈顶值赋值给本地变量 int var = 99;
                        mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); // 将静态变量System.out压倒栈顶
                        mv.visitVarInsn(ILOAD, intVar1); // 将整数变量压到栈顶
                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false); // 出栈System.out,整数变量,并调用sout(int)完成输出
//
//                        int intVar2 = newLocal(Type.INT_TYPE);
//                        mv.visitTypeInsn(NEW, "java/lang/String"); // new String()然后推到栈顶
//                        mv.visitInsn(DUP); // 复制栈顶元素
                        int nextLocal = this.nextLocal; // 获取当前可用的局部变量表位置??
                        mv.visitLdcInsn("pppp"); // 加载常量到栈顶
                        mv.visitVarInsn(ASTORE, nextLocal); // 将栈顶string对象复制给局部变量
//                        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "<init>", "(Ljava/lang/String;)V", false); // 调用构造函数完成string对象创建
//                        mv.visitVarInsn(ASTORE, this.nextLocal);
                        mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); // 将静态变量System.out压倒栈顶
                        mv.visitVarInsn(ALOAD, nextLocal); // 将string变量压到栈顶
                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); // 出栈System.out,整数变量,并调用sout(string)完成输出
                    }
                    public void visitMaxs(int maxStack, int maxLocals) {
                        super.visitMaxs(maxStack, maxLocals);
                    }
                    protected void onMethodExit(int opcode) {
                        super.onMethodExit(opcode);
                    }
                };
            }
        }, ClassReader.EXPAND_FRAMES);
        // 获取后的字节码
        byte[] byteAfterInstrument = cw.toByteArray();
        outputClazz(byteAfterInstrument, "xxvv");
        // 测试方法
        Class<?> clazz = new
                YY().defineClass("com.dahuyuo.asmtest.XX", byteAfterInstrument, 0, byteAfterInstrument.length);
        Method didi = clazz.getMethod("didi");
        didi.invoke(clazz.newInstance());
    }
    private static void outputClazz(byte[] bytes, String className) {
        // 输出类字节码
        FileOutputStream out = null;
        try {
            String pathName = YY.class.getResource("/").getPath() + className + "_after_instrument.class";
            out = new FileOutputStream(new File(pathName));
            System.out.println("插桩后代码输出路径:" + pathName);
            out.write(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != out) try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
 
用来进行插桩的XX类如下:
public class XX {
  /*  public static void main(String[] args) {
        int a = 10;
        int b = 20;
        System.out.println(a + b);
    }*/
    public void didi() {
    }
}
 
运行测试:
 
 查看生成的字节码:
 
 就是我们要的效果。
写在后面
参考文章列表
JVM 虚拟机字节码指令表 。



















