前言
最近在研究一个测试工具,测试工具的实现思路是,通过生产上录制接口出入参报文,同时录制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、结果
总结
以上是利用asm来实现的一个动态代理,同时这也是spring中aop的底层原理,利用asm还可以做很多事情,比如代码生成等。