写在前面
本文看下如何通过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 虚拟机字节码指令表 。