神奇的asm框架

0.代码结构

1.目标类

/**
 * @Description 目标对象测试类
 * @Date 2021-02-23 15:23
 */
public class Test {
    int i = 0;

    public int getI() {
        return this.i;
    }
}

2.自定义类加载器

/**
 * @Description 自定义类加载器
 * @Date 2021-02-23 14:46
 */
public class MyClassLoader extends ClassLoader {
    public Class defineClass(String className, byte[] classBytes) {
        return super.defineClass(className, classBytes, 0, classBytes.length);
    }

}

3.类的打印

import org.objectweb.asm.*;

import java.io.IOException;

import static org.objectweb.asm.Opcodes.ASM4;

/**
 * @Description asm 打印类  继承ClassVisitor
 * @Date 2021-02-23 15:12
 */
public class MyClassPrint extends ClassVisitor {
    public MyClassPrint() {
        super(ASM4);
    }

    @Override
    public void visit(int version, int access, String name,
                      String signature, String superName, String[] interfaces) {
        System.out.println(name + " extends " + superName + " {");
    }

    public void visitSource(String source, String debug) {
    }

    public void visitOuterClass(String owner, String name, String desc) {
    }

    public AnnotationVisitor visitAnnotation(String desc,
                                             boolean visible) {
        return null;
    }

    public void visitAttribute(Attribute attr) {
    }

    public void visitInnerClass(String name, String outerName,
                                String innerName, int access) {
    }

    public FieldVisitor visitField(int access, String name, String desc,
                                   String signature, Object value) {
        System.out.println(" " + desc + " " + name);
        return null;
    }

    public MethodVisitor visitMethod(int access, String name,
                                     String desc, String signature, String[] exceptions) {
        System.out.println(" " + name + desc);
        return null;
    }

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

    public static void main(String[] args) throws IOException {
        MyClassPrint cp = new MyClassPrint();//cp中相对应的visit方法 写自己想要做的事情
        //读取jdk自带的类
        ClassReader cr = new ClassReader("java.lang.Runnable");//cr去读取目标类class
        cr.accept(cp, 0);//read通过visit去读取.class内容,所以我们在visit中写想要进行的方法
        //读取自己的类
        ClassReader cr2 = new ClassReader(MyClassPrint.class.getClassLoader().getResourceAsStream("asm/Test.class"));
        cr2.accept(cp, 0);
    }
}

4.类的生成

import org.objectweb.asm.ClassWriter;

import static org.objectweb.asm.Opcodes.*;

/**
 * @Description 生成类
 * @Date 2021-02-23 15:26
 * 类的实际样子:
 * package asm;
 * public interface Comparable {
 * int LESS = -1;
 * int EQUAL = 0;
 * int GREATER = 1;
 * int compareTo(Object o);
 * }
 */
public class MyClassWriter {
    public static void main(String[] args) {
        ClassWriter cw = new ClassWriter(0);// ClassWriter继承ClassVisitor
        //相当于在设置.class信息
        //生成基础信息1.5版本, public abstract interface Comparable类,路径在asm包下, 父类Object
        cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,
                "asm/Comparable", null, "java/lang/Object",
                null);
        //生成属性  名字less  I表示int  默认值-1
        cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "LESS", "I",
                null, new Integer(-1)).visitEnd();
        //生成属性
        cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "EQUAL", "I",
                null, new Integer(0)).visitEnd();
        //生成属性
        cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "GREATER", "I",
                null, new Integer(1)).visitEnd();
        //生成方法 compareTo 参数Object  返回值I int
        cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "compareTo",
                "(Ljava/lang/Object;)I", null, null).visitEnd();
        //结束设置
        cw.visitEnd();

        byte[] b = cw.toByteArray();
        MyClassLoader cl = new MyClassLoader();
        Class c = cl.defineClass("asm.Comparable", b); //加载生成的类   注意没有生成实际文件
        System.out.println(c.getMethods()[0].getName());
    }
}

5.代理方法对象

/**
 * @Description
 * @Date 2021-02-23 16:07
 */
public class TimeProxy {
    //需要插入的代理方法
    public static void before() {
        System.out.println("tp before");
    }
}

6.动态代理

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;

import java.io.File;
import java.io.FileOutputStream;

import static org.objectweb.asm.Opcodes.ASM4;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;


/**
 * @Description 动态代理底层
 * @Date 2021-02-23 15:54
 */
public class MyClassTransFormer {
    public static void main(String[] args) throws Exception {
        ClassReader cr = new ClassReader(MyClassPrint.class.getClassLoader().getResourceAsStream("asm/Test.class"));
        ClassWriter cw = new ClassWriter(0);
        ClassVisitor cv = new ClassVisitor(ASM4, cw) {//把classwriter包一层 cv->cw
            public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
                return new MethodVisitor(ASM4, mv) {
                    @Override
                    public void visitCode() {
//                        System.out.println("                 "+name+"              ");
                        if (name.equals("getI")) {//只在getI加before方法 构造器不加
                            //在原方法前,添加方法
                            visitMethodInsn(INVOKESTATIC, "asm/TimeProxy", "before", "()V", false);
                        }
                        //原方法
                        super.visitCode();
                    }
                };
            }
        };
        cr.accept(cv, 0);//cr->cv->cw    cv起桥梁的作用,把cr和cw连通
        byte[] b2 = cw.toByteArray();//获得修改后的类的二进制字节

        //生成class文件
        String path = (String) System.getProperties().get("user.dir");
//        System.out.println(path);
        File f = new File(path + "/asm/");
        f.mkdirs();
        FileOutputStream fos = new FileOutputStream(new File(path + "/asm/Test.class"));
        fos.write(b2);
        fos.flush();
        fos.close();
    }
}

个人理解:对于asm来说,我们写好的类编译后的class文件,就像一块块拼起来的积木,asm可以对这个积木进行重新拼装。

asm官网,可以查看官网中的asm4-guide.pdf。若打开太慢,链接: https://pan.baidu.com/s/1---_yuxqi8OQScDnb53ctw 提取码: p4qn 

B站马士兵视频讲解 36分钟左右开始讲asm部分

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值