ASM的使用demo

251 阅读2分钟

前言

最近在研究一个测试工具,测试工具的实现思路是,通过生产上录制接口出入参报文,同时录制orm层的curd方法出入参,以及redis和第三方接口的出入参,最后通过回放的方式来达到接口逻辑回归的目的。

实现方案

1、利用动态代理的思路,在目标方法的前后添加逻辑,保存出入参到数据库或者其他介质中。
2、利用java 提供的agent技术在运行时动态织入代码。

ASM示例

1、该类中的方法将要添加到目标类方法前后。

public class AopInteceptor {

    public static void before(){
        System.out.println(".......before().......");
    }

    public static void after(){
        System.out.println(".......after().......");
    }
}

2、构建代理对象

public class AopClassAdapter extends ClassVisitor implements Opcodes {
    public AopClassAdapter(int i, ClassVisitor classVisitor) {
        super(i, classVisitor);
    }

    public void visit(
            int version,
            int access,
            String name,
            String signature,
            String superName,
            String[] interfaces) {
        //更改类名,并使新类继承原有的类。
        super.visit(version, access, name + "_tmp", signature, name, interfaces);
        {//输出一个默认的构造方法
            MethodVisitor mv = super.visitMethod(ACC_PUBLIC, "<init>",
                    "()V", null, null);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, name, "<init>", "()V");
            mv.visitInsn(RETURN);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }
    }

    public MethodVisitor visitMethod(
            int access,
            String name,
            String desc,
            String signature,
            String[] exceptions) {
        if ("<init>".equals(name))
            return null;//放弃原有类中所有构造方法
        if (!name.startsWith("asm"))
            return null;// 只对asm开始的方法执行代理

        MethodVisitor mv = super.visitMethod(access, name,
                desc, signature, exceptions);
        return new AopMethodVisitor(this.api, mv);
    }
}

3、增强代理对象

public class AopMethodVisitor extends MethodVisitor implements Opcodes {
    public AopMethodVisitor(int i, MethodVisitor methodVisitor) {
        super(i, methodVisitor);
    }

    public void visitCode(){
        super.visitCode();
        this.visitMethodInsn(INVOKESTATIC,"com/cz/asm/AopInteceptor","before","()V",false);
    }

    public void visitInsn(int opcode) {
        if (opcode >= IRETURN && opcode <= RETURN)//在方法返回之前
        {
            this.visitMethodInsn(INVOKESTATIC, "com/cz/asm/AopInteceptor", "after", "()V", false);
        }
        super.visitInsn(opcode);
    }
}

4、加载代理对象

public class AopClassLoader extends ClassLoader implements Opcodes {

    public AopClassLoader() {
        super();
    }

    public AopClassLoader(ClassLoader parent) {
        super(parent);
    }

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if (!name.endsWith("_tmp"))
            return super.loadClass(name);
        try {
            ClassWriter cw = new ClassWriter(0);
            InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("com/cz/asm/TestBean.class");
            ClassReader reader = new ClassReader(is);
            reader.accept(new AopClassAdapter(ASM4, cw), ClassReader.SKIP_DEBUG);
            byte[] code = cw.toByteArray();

//            // -----打印构造后的类
//            FileOutputStream fos = new FileOutputStream("E:\code\java\TestBean_tmp.class");
//            fos.write(code);
//            fos.flush();
//            fos.close();

            return this.defineClass(name, code, 0, code.length);
        } catch (Throwable e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
//        return null;
    }
}

5、被代理对象

public class TestBean {
    public void asmEcho(){
        System.out.println("hello asm");
    }
}

6、测试

public class Main {

    public static void main( String[] args ) throws IOException {
        try {
            AopClassLoader classLoader = new AopClassLoader();
            Class<?> asmClass = classLoader.loadClass("com.cz.asm.TestBean_tmp");
            Map map = new HashMap<String, String>();
            Object obj = asmClass.newInstance();
            Method method = asmClass.getMethod("asmEcho",null);
            method.invoke(obj,null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

7、结果

image.png

总结

以上是利用asm来实现的一个动态代理,同时这也是spring中aop的底层原理,利用asm还可以做很多事情,比如代码生成等。