ASM模拟AOP
ASM是一个通用的Java字节码操作和分析框架。它可以直接以二进制形式用于修改现有类或动态生成类。ASM提供了一些常见的字节码转换和分析算法,可以从中构建定制的复杂转换和代码分析工具。
maven依赖
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.1</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
<version>9.1</version>
</dependency>
目标实现逻辑
public class App {
public void method() {
long start=System.currentTimeMillis();
System.out.println("invoke method start......");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
long end=System.currentTimeMillis();
System.out.println("method user time == "+(end-start)+"ms");
}
}
想在方法之前和之后打印时间,最终计算耗时。此时在IDEA中可以利用ASM插件获取ASMified文件。通过和原始的ASMified对比,然后抽取我们想要增强的ClassFile的指令集。
实现ClassVisitor和MethodVisitor
public class MyVisitor extends ClassVisitor {
public MyVisitor(ClassVisitor classVisitor) {
super(Opcodes.ASM9, classVisitor);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
cv.visit(version, access, name, signature, superName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
//这里开始对方法进行增强
MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);
if (!"<init>".equals(name) && mv != null) {
//对于特定的方法增强
mv=new MyMethodVisitor(mv);
}
return mv;
}
/**
* 方法访问类
*/
class MyMethodVisitor extends MethodVisitor {
public MyMethodVisitor(MethodVisitor methodVisitor) {
super(Opcodes.ASM9, methodVisitor);
}
@Override
public void visitCode() {
mv.visitCode();
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
mv.visitVarInsn(Opcodes.LSTORE, 1);
}
@Override
public void visitInsn(int opcode) {
if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) ||
opcode == Opcodes.ATHROW) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
mv.visitVarInsn(Opcodes.LSTORE, 3);
Label label7 = new Label();
mv.visitLabel(label7);
mv.visitLineNumber(14, label7);
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitVarInsn(Opcodes.LLOAD, 3);
mv.visitVarInsn(Opcodes.LLOAD, 1);
mv.visitInsn(Opcodes.LSUB);
mv.visitInvokeDynamicInsn("makeConcatWithConstants", "(J)Ljava/lang/String;", new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/StringConcatFactory", "makeConcatWithConstants", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;", false), new Object[]{"method user time == \u0001ms"});
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
}
mv.visitInsn(opcode);
}
}
}
增强输出class
public class Generator {
public static void main(String[] args) throws IOException {
ClassReader cr = new ClassReader("com/gosaint/asm/App");
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = new MyVisitor(cw);
//跳过调试
cr.accept(cv, ClassReader.SKIP_DEBUG);
byte[] data = cw.toByteArray();
File f = new File("target/classes/com/gosaint/asm/App.class");
FileOutputStream fos = new FileOutputStream(f);
fos.write(data);
fos.close();
System.out.println("Generator App class success!!!");
}
}
测试
public class MainTest {
public static void main(String[] args) throws InterruptedException {
App a=new App();
a.method();
}
}