ASM 在方法进入时候添加语句和方法退出的时候添加语句

1 写一个实体类

package com.org.xcyz.asm;

public class Student{
    private int id;
    private String name;
    private boolean sex;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isSex() {
        return sex;
    }

    public void setSex(boolean sex) {
        this.sex = sex;
    }
}

2 继承 MethodVisitor

package com.org.xcyz.asm.trans;


import org.objectweb.asm.MethodVisitor;

import static com.org.xcyz.asm.CommonTypes.PRINT_STREAM_TYPE;
import static com.org.xcyz.asm.CommonTypes.SYSTEM_TYPE;
import static org.objectweb.asm.Opcodes.ATHROW;
import static org.objectweb.asm.Opcodes.GETSTATIC;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.RETURN;

public class StopWatchAdapter extends MethodVisitor {
    public StopWatchAdapter(int api,MethodVisitor mv) {
        super(api,mv);
    }

    @Override
    public void visitCode() {
        super.visitFieldInsn(GETSTATIC, SYSTEM_TYPE.getInternalName(),"out", PRINT_STREAM_TYPE.getDescriptor());
        super.visitLdcInsn("Enter method");
        super.visitMethodInsn(INVOKEVIRTUAL,PRINT_STREAM_TYPE.getInternalName(),"println","(Ljava/lang/String;)V",false);
        super.visitCode();
    }

    @Override
    public void visitInsn(int opcode) {
       if(opcode == ATHROW||(opcode>=IRETURN&&opcode<=RETURN)){
           super.visitFieldInsn(GETSTATIC, SYSTEM_TYPE.getInternalName(),"out", PRINT_STREAM_TYPE.getDescriptor());
           super.visitLdcInsn("Exit method");
           super.visitMethodInsn(INVOKEVIRTUAL,PRINT_STREAM_TYPE.getInternalName(),"println","(Ljava/lang/String;)V",false);
       }
        super.visitInsn(opcode);
    }
}

3 添加 常用Type类用来简化描述符以及internal name的书写

package com.org.xcyz.asm;


import org.objectweb.asm.Type;

import java.io.PrintStream;

public interface CommonTypes {
   Type SYSTEM_TYPE = Type.getType(System.class);
   Type PRINT_STREAM_TYPE = Type.getType(PrintStream.class);
}

4 修改原始Student类并重命名

package com.org.xcyz.asm.trans;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.ClassRemapper;
import org.objectweb.asm.commons.Remapper;
import org.objectweb.asm.commons.SimpleRemapper;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;

public class AddVisitorTransformer1 {
    public static void main(String[] args) throws IOException {
        ClassReader cr = new ClassReader("com.org.xcyz.asm.Student");
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS);
        final String newClassName = "com/org/xcyz/asm/trans/StudentAddition";
        //(3)串连ClassVisitor
        Remapper remapper = new SimpleRemapper("com/org/xcyz/asm/Student",newClassName );
        ClassVisitor cv = new ClassRemapper(cw, remapper);
        ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM9,cv) {
            @Override
            public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
                boolean isNativeMethod = (access & ACC_NATIVE) == ACC_NATIVE;
                boolean isAbstract = (access & ACC_ABSTRACT) == ACC_ABSTRACT;
                if (methodVisitor != null && !"<init>".equals(name)&&!isNativeMethod && !isAbstract) {
                    return new StopWatchAdapter(api,methodVisitor);
                }
                return methodVisitor;
            }
        };
        cr.accept(classVisitor,ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);
        byte[] bytes = cw.toByteArray();
        Files.write(Paths.get("target/classes/"+newClassName+".class"),bytes,TRUNCATE_EXISTING,CREATE);
    }
}

5 反射调用新生成的 StudentAddition 类

package com.org.xcyz.asm.run;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class StudentRun {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> aClass = Class.forName("com.org.xcyz.asm.trans.StudentAddition");
        Object o = aClass.newInstance();
        Method getName = aClass.getDeclaredMethod("getName");
        Method setName = aClass.getDeclaredMethod("setName",new Class[]{String.class});
        setName.invoke(o,"Yang");
        Object result = getName.invoke(o);
        System.out.println(result);
    }
}

6 输出如下

Enter method
Exit method
Enter method
Exit method
Yang

Process finished with exit code 0

总结:

在从 ClassReader 到 ClassVisitor 到ClassWriter的过程中, 由 ClassReader来触发各种visitXXX事件,在此过程中如果没有加入任何的ClassVisitor亦或是ClassVisitor只是trace等操作并没有在这个过程中做任何的拦截,那么最终调用的ClassWriter的visitXXX方法,此时将会把原来的class信息原封不动的写入。如果在此过程中对visiteXxx方法做了拦截,例如在visit的时候修改version在visiteFiled的时候判断时候有某一个字段,最后在visitEnd的时候去添加字段或者添加方法。

如果想对方法本身做拦截的话,那么就需要对MethodVisitor做一个Proxy.实质上最终是代理了ClassWriter返回的MethodVisitor.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值