asm杂耍第一弹,使用asm实现动态代理

asm杂耍第一弹,使用asm实现动态代理

asm:用来操作字节码的框架,可以对class文件进行CRUD,听这名字就知道是什么东西,在写c代码的时候来一句ASM装逼,这个就像java的汇编一样。

User接口

/**
 * @author authorZhao
 * @date 2019/12/30
 */
public interface User {

    /**
     * 获取名字
     * @return
     */
    String getName();

    /**
     * 设置名字
     * @param name
     */
    void setName(String name);

    /**
     * 获取年龄
     * @return
     */
    Integer getAge();

    /**
     * 设施自年龄
     * @param age
     */
    void setAge(Integer age) ;

}

测试代码

/**
 * @author authorZhao
 * @date 2019/12/30
 */
public class TestUser {
    public static void main(String[] args) {
        try {
            Class uClass = UserImplUtils.getClass("com.qdz.proxy.asm.test1.UserImpl", User.class);
            User user = (User)uClass.getConstructors()[0].newInstance();
            user.setName("渣渣辉");
            user.setAge(385);
            System.out.println("带噶好,我系"+user.getName()+",我今天"+user.getAge()+"岁");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

User实现类

上文在不采用jdk和从cglib动态代理的情况下生成了user实现类,采用的是asm框架

上代码

/**
 * @author authorZhao
 * @date 2019/12/30
 */
public class UserImplUtils {

    /**
     * 根据类全名和接口名生成class文件
     * @param className 类全名,注意是用点分隔的,采用idea的
     * @param clazz 接口class对象 采用点分隔
     * @return
     * @throws Exception
     */
    private static byte[] dump(String className,Class clazz) throws Exception {
        String className2 = className.replace(".","/");
        String sourceName = className.substring(className.lastIndexOf(".")+1)+".java";
        //模仿常量池的语法
        String constClassName = "L"+className2+";";//Lcom/qdz/proxy/asm/test1/UserImpl;

        ClassWriter cw = new ClassWriter(0);
        FieldVisitor fv;
        MethodVisitor mv;
        AnnotationVisitor av0;
        //声明需要实现的类
        //jdk版本 11,权限,这个不懂,类名,签名,父类名,接口名
        cw.visit(55, ACC_PUBLIC + ACC_SUPER, className2, null, "java/lang/Object", new String[]{clazz.getName().replace(".","/")});
        //
        cw.visitSource(sourceName, null);

        //设置属性 name
        // age
        {
            fv = cw.visitField(ACC_PRIVATE, "name", "Ljava/lang/String;", null, null);
            fv.visitEnd();
        }
        {
            fv = cw.visitField(ACC_PRIVATE, "age", "Ljava/lang/Integer;", null, null);
            fv.visitEnd();
        }
        //方法
        {
            //构造方法,语法固定
            mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
            mv.visitCode();
            Label l0 = new Label();
            mv.visitLabel(l0);
            mv.visitLineNumber(7, l0);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
            mv.visitInsn(RETURN);
            Label l1 = new Label();
            mv.visitLabel(l1);
            //这个this是固定写法
            mv.visitLocalVariable("this", constClassName, null, l0, l1, 0);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }
        //getName方法
        {
            mv = cw.visitMethod(ACC_PUBLIC, "getName", "()Ljava/lang/String;", null, null);
            mv.visitCode();
            Label l0 = new Label();
            mv.visitLabel(l0);
            mv.visitLineNumber(14, l0);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETFIELD, className2, "name", "Ljava/lang/String;");
            mv.visitInsn(ARETURN);
            Label l1 = new Label();
            mv.visitLabel(l1);
            mv.visitLocalVariable("this", constClassName, null, l0, l1, 0);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }
        //setName方法
        {
            mv = cw.visitMethod(ACC_PUBLIC, "setName", "(Ljava/lang/String;)V", null, null);
            mv.visitCode();
            Label l0 = new Label();
            mv.visitLabel(l0);
            mv.visitLineNumber(18, l0);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitFieldInsn(PUTFIELD, className2, "name", "Ljava/lang/String;");
            Label l1 = new Label();
            mv.visitLabel(l1);
            mv.visitLineNumber(19, l1);
            mv.visitInsn(RETURN);
            Label l2 = new Label();
            mv.visitLabel(l2);
            mv.visitLocalVariable("this", constClassName, null, l0, l2, 0);
            mv.visitLocalVariable("name", "Ljava/lang/String;", null, l0, l2, 1);
            mv.visitMaxs(2, 2);
            mv.visitEnd();
        }
        //getAge方法
        {
            mv = cw.visitMethod(ACC_PUBLIC, "getAge", "()Ljava/lang/Integer;", null, null);
            mv.visitCode();
            Label l0 = new Label();
            mv.visitLabel(l0);
            mv.visitLineNumber(22, l0);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETFIELD, className2, "age", "Ljava/lang/Integer;");
            mv.visitInsn(ARETURN);
            Label l1 = new Label();
            mv.visitLabel(l1);
            mv.visitLocalVariable("this", constClassName, null, l0, l1, 0);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }
        //setAge方法
        {
            mv = cw.visitMethod(ACC_PUBLIC, "setAge", "(Ljava/lang/Integer;)V", null, null);
            mv.visitCode();
            Label l0 = new Label();
            mv.visitLabel(l0);
            mv.visitLineNumber(26, l0);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitFieldInsn(PUTFIELD, className2, "age", "Ljava/lang/Integer;");
            Label l1 = new Label();
            mv.visitLabel(l1);
            mv.visitLineNumber(27, l1);
            mv.visitInsn(RETURN);
            Label l2 = new Label();
            mv.visitLabel(l2);
            mv.visitLocalVariable("this", constClassName, null, l0, l2, 0);
            mv.visitLocalVariable("age", "Ljava/lang/Integer;", null, l0, l2, 1);
            mv.visitMaxs(2, 2);
            mv.visitEnd();
        }
        cw.visitEnd();

        return cw.toByteArray();
    }

    /**
     * 根据类名和接口返回对应的class对象
     * @param className 类全名,注意是用点分隔的,采用idea的
     * @param clazz 接口class对象 采用点分隔
     * @return
     * @throws Exception
     */
    public static Class<?> getClass(String className,Class clazz ) throws Exception {
        byte[] classData = dump(className, clazz);
        return  new MyClassLoader().defineClassForName(className, classData);
    }
}

类加载器

/**
 * 自定义类加载器
 */
public class MyClassLoader extends ClassLoader {
    public MyClassLoader() {
        super(Thread.currentThread().getContextClassLoader());
    }

    /**
     * 吧class数组转化为Class对象
     * @param name 类全名
     * @param data class数组
     * @return
     */
    public Class<?> defineClassForName(String name, byte[] data) {
        return this.defineClass(name, data, 0, data.length);
    }

}

说明:

  • 为什么要定义接口

    • 为了编辑器方便,免得反射方法名太麻烦
  • 为什么这么生成

    • 哈哈,我当然不会,先自己实现以下接口然使用asm工具生成代码
  • 原创文章,转载请申明

附录:class文件结构

参考文章,oracle官网

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;//55表示jdk11
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}
发布了42 篇原创文章 · 获赞 13 · 访问量 8296

猜你喜欢

转载自blog.csdn.net/weixin_43328357/article/details/103768895
ASM