我是靠谱客的博主 傲娇小伙,最近开发中收集的这篇文章主要介绍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.

最后

以上就是傲娇小伙为你收集整理的ASM 在方法进入时候添加语句和方法退出的时候添加语句的全部内容,希望文章能够帮你解决ASM 在方法进入时候添加语句和方法退出的时候添加语句所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部