asm中的ClassWriter使用、自定义ClassVisitor和cglib的enhancer使用实例

ASM是Java中比较流行的用来读写字节码的类库,用来基于字节码层面对代码进行分析和转换。在读写的过程中可以加入自定义的逻辑以增强或修改原来已编译好的字节码,比如CGLIB用它来实现动态代理。ASM被设计用于在运行时对Java类进行生成和转换,当然也包括离线处理。ASM短小精悍、且速度很快,从而避免在运行时动态生成字节码或转换时对程序速度的影响,又因为它体积小巧,可以在很多内存受限的环境中使用。

ClassWriter的使用

ClassVisitor是用来生成asm和改变字节码的,ClassVisitor是一
个访问字节码的框架,其对字节码的创建和修改主要是通过其内部的ClassVisitor具体实现来代理的
ClassWriter:ClassWriter 是ClassVisitor的一个实现类

ClassWriter提供了一类visit方法来编写类。使用visit方法开始编写,visitEnd方法结束编写。

具体的来说,此类visit方法能够为需要修改的类添加属性和方法。例如,visitMethod可以为类添加方法,visitAttribute可以为类添加属性。

import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
  public static void main(String[] args) throws IOException {
        ClassWriter cs = new ClassWriter(0);
        //通过vist确定类的同步信息 java版本号 类修饰符 类的权限定名
        cs.visit(Opcodes.V1_8,Opcodes.ACC_PUBLIC,"HZYziyi",null, "java/lang/Object",null);
        //构造函数
        MethodVisitor mv = cs.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();
        // 定义code方法
        MethodVisitor methodVisitor = cs.visitMethod(Opcodes.ACC_PUBLIC, "code", "()V", null, null);
        methodVisitor.visitCode();
        methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        methodVisitor.visitLdcInsn("I'm a哈哈哈 Duck,Just Coding.....");
        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
        methodVisitor.visitInsn(Opcodes.RETURN);
        methodVisitor.visitMaxs(2, 2);
        methodVisitor.visitEnd();
        cs.visitEnd();
        // 使classWriter类已经完成
        // 将classWriter转换成字节数组写到文件里面去
        byte[] data = cs.toByteArray();
        File file = new File("./HZYziyi.class");
        FileOutputStream fout = new FileOutputStream(file);
        fout.write(data);
        fout.close();
    }

运行结束和可以看到目录下生成了一个HZYziyi.class文件,可以看到其中的内容:
在这里插入图片描述

自定义一个ClassVisitor

写一个类继承ClassVisitor

public class ClassChangeAdapter extends ClassVisitor {

    public ClassChangeAdapter(ClassVisitor classVisitor) {
        super(ASM4, classVisitor);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        // 如果当前访问到main方法就移除掉
        if(name.equals("main")) {
            return null;
        }
        MethodVisitor mv =super.visitMethod(access, name,
                desc, signature, exceptions);
        MethodChangeAdaper methodChangeAdaper = new MethodChangeAdaper(ASM4, mv, access, name, desc);
        return methodChangeAdaper;
    }

}

自定义MethodChangeAdaper类继承AdviceAdapter

public class MethodChangeAdaper extends AdviceAdapter {
    /**
     * Creates a new {@link AdviceAdapter}.
     *
     * @param api    the ASM API version implemented by this visitor. Must be one
     *               of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
     * @param mv     the method visitor to which this adapter delegates calls.
     * @param access the method's access flags (see {@link Opcodes}).
     * @param name   the method's name.
     * @param desc   the method's descriptor (see {@link Type Type}).
     */
    protected MethodChangeAdaper(int api, MethodVisitor mv, int access, String name, String desc) {
        super(api, mv, access, name, desc);
    }

    @Override
    public void visitCode() {
        super.visitCode();

        //调用静态方法
        mv.visitMethodInsn(INVOKESTATIC, "com/tuling/cglib/proxy/DaoAnotherProxy", "before", "()V", false);

        // 创建新的局部变量表索引
        // 将int型2推送至操作数栈顶
        mv.visitInsn(ICONST_2);
        /// 创建新的局部变量表索引
        int newLocal =  super.newLocal(jdk.internal.org.objectweb.asm.Type.INT_TYPE);
        mv.visitIntInsn(ISTORE, newLocal);
        mv.visitIincInsn(newLocal, 1);
        mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        mv.visitIntInsn(ILOAD, newLocal);
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
           mv.visitMethodInsn(INVOKESTATIC, "com/tuling/cglib/proxy/DaoAnotherProxy", "after", "()V", false);
    }
}

在mv.visitMethodInsn(INVOKESTATIC, “com/tuling/cglib/proxy/DaoAnotherProxy”, “before”, “()V”, false);
这句话中调用了自己的包下这个DaoAnotherProxy类的before方法
其中DaoAnotherProxy 类:

其中public class DaoAnotherProxy {

    public static void before(){
        System.out.println("前置通知!");
    }


    public static void after(){
        System.out.println("后置通知!");
    }
}

主类:

public class ClassChangeMethodTest {
    public static void main(String[] args) throws Exception {
        ClassWriter cw = new ClassWriter(COMPUTE_MAXS);
        ClassReader cr = new ClassReader(TestmyQuestion.class.getName());

        ClassChangeAdapter adapter=new ClassChangeAdapter(cw);

        cr.accept(adapter,ClassReader.SKIP_FRAMES);

        byte[] bytes = cw.toByteArray();

        File file = new File("./TestmyQuestion.class");
        FileOutputStream fout = new FileOutputStream(file);
        fout.write(bytes);
        fout.close();



        MyClassLoader classLoader=new MyClassLoader();
        Class loadedClass = classLoader.defineClass(TestmyQuestion.class.getName(), bytes);

        loadedClass.newInstance();
        //创建由此Class对象表示的类的新实例。 该类被实例化为一个带有空参数列表的new表达式。 如果尚未初始化该类,则将其初始化
    }
}

其中TestmyQuestion 类:

public class TestmyQuestion {
    public static void main(String[] args) {
        int i=2;
        i++;
        System.out.println(i);
    }
}

上面的程序运行以后可以看到目录下生成了一个class文件TestmyQuestion.class:


import com.tuling.cglib.proxy.DaoAnotherProxy;

public class TestmyQuestion {
    public TestmyQuestion() {
        DaoAnotherProxy.before();
        byte var1 = 2;
        int var2 = var1 + 1;
        System.out.println(var2);
        DaoAnotherProxy.after();
        super();
    }
}

这即是asm生成的class文件
输出:
在这里插入图片描述

enhancer的使用

自定义MethodInterceptor :


/**
 * 创建一个Dao代理
 */
public class DaoProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("begin intercept");
        //invokeSuper方法调用目标类的方法
        proxy.invokeSuper(obj, args);
        System.out.println("end intercept");
        return obj;
    }
}

自定义CallbackFilter


import net.sf.cglib.proxy.CallbackFilter;

import java.lang.reflect.Method;

/**
 * 返回数值表示顺序
 */
public class DaoFilter implements CallbackFilter {
    @Override
    public int accept(Method method) {
        System.out.println("accept");
        if("select".equalsIgnoreCase(method.getName())) {
            return 0;
        } else if ("delete".equalsIgnoreCase(method.getName())) {
            return 1;
        }
        return 2;
    }
}

接口和实现类:

public interface IDao {
    public void select();

    public void insert();

}

public class Dao implements IDao{



    @Override
    public void select() {
        System.out.println("select 1 from dual:");
        insert();
    }
    @Override
    public void insert() {
        System.out.println("insert into ...");
    }


}

主程序:


public class Client {
    public static void main(String[] args) {


        //将代理类存到本地磁盘
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./");
        //实例化增强器
        Enhancer enhancer = new Enhancer();
        //设置需要代理的目标类
        enhancer.setSuperclass(Dao.class);
        //设置拦截对象 回调的实现类
        enhancer.setCallback(new DaoProxy());
        //使用create 返回Object 生成代理类并返回实例
        Dao dao = (Dao) enhancer.create();
        //select优先级高 使用DaoProxy
        dao.select();
        //无法代理被final修饰的方法
        //dao.delete();
       // dao.insert();

    }
}

运行主程序可以看到输出:
在这里插入图片描述

生成的代理类class文件:


public class Dao implements IDao {
    public Dao() {
    }

    public void select() {
        DaoAnotherProxy.before();
        System.out.println("select 1 from dual");
    }

    public void insert() {
        System.out.println("insert into ...");
    }

    public final void delete() {
        System.out.println("delete from ...");
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值