• 再叙ASM


         上一篇文章,我们已体验到ASM的威力,那么结合上面的代码解释ASM是怎么执行的。

    ClassWriter clazzWriter = new ClassWriter(0);  

         首先看下官方文档对ClassWriter的描述:

    A ClassVisitor that generates classes in bytecode form. More precisely this visitor generates a byte array conforming to the Java class file format. It can be used alone, to generate a Java class "from scratch", or with one or more ClassReader and adapter class visitor to generate a modified class from one or more existing Java classes

     方法的栈长度和本地变量表长度用户自己计算(???怎么算);

    clazzWriter.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, "com/sunchao/asm/HelloWorld", null, "java/lang/Object", null);  

    解释下方法中参数的意思:

    • Opcodes.V1_5指定类的版本
    • Opcodes.ACC_PUBLIC表示这个类是public
    • “com/sunchao/asm/HelloWorld”类的全限定名称
    • 第一个null位置变量定义的是泛型签名
    • “java/lang/Object”这个类的父类
    • 第二个null位子的变量定义的是这个类实现的接口
            MethodVisitor mv = clazzWriter.visitMethod(Opcodes.ACC_PUBLIC , "sayHello",   
                   "()V" , null, null);//新增加一个方法  
            mv.visitCode();//启动访问字节码  
            //在java/lang/System上添加一个Ljava/io/PrintStream类型的字段‘out’  
            mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");  
            mv.visitLdcInsn("Hello World!");//将常量池中的字符串常量加载到栈顶 ,输出字符串内容  
            //调用java/io/PrintStream中的println()方法  
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");    
            mv.visitInsn(Opcodes.RETURN);//设置返回值  
            mv.visitMaxs(0, 0);//设置方法的栈和本地变量表的大  
            mv.visitEnd();//结束访问  

        代码中的注释,可以让我们看个大概了吧。

        上面的代码中我们看到了如何使用ASM生成一个简单的JAVA类,里面使用到了很多的基本概念,比如:方法描述、引用描述

    一、类版本:

        一个Java二进制的类文件,都有一个版本,因此ASM中提供了几个常量来指定一个类的版,这些常量定义在org.objectweb.asm.Opcodes接口中,如下:

    // versions  
      
        int V1_1 = 3 << 16 | 45;  
        int V1_2 = 0 << 16 | 46;  
        int V1_3 = 0 << 16 | 47;  
        int V1_4 = 0 << 16 | 48;  
        int V1_5 = 0 << 16 | 49;  
        int V1_6 = 0 << 16 | 50;  
        int V1_7 = 0 << 16 | 51;  

     二、JVM内部名字

          在Java二进制文件中使用的是JVM的内部名字,而不是我们所熟悉的以“.”分割的全限定名,内部名字是以“/”替代“.”的全名,例如:java.lang.String在JVM中的内部名字是java/lang/String。在ASM中可以使用org.objectweb.asm.Type类中的静态方法getInternalName(final Class c) 来获得,如下:

    public class InternalNameTransform {  
        public static void main(String[] args) {  
            System.out.println(Type.getInternalName(String.class));  
            System.out.println(Type.getInternalName(Integer.class));  
            System.out.println(Type.getInternalName(InternalNameTransform.class));  
        }  
    }  

      运行结果:

    java/lang/String  
    java/lang/Integer  
    com/sunchao/asm/InternalNameTransform  

    三、类型描述

        我们知道JAVA类型分为基本类型和引用类型,在JVM中对每一种类型都有与之相对应的类型描述,如下表:

    Java类型 JVM中的描述
    boolean Z
    char C
    byte B
    short S
    int I
    float F
    long J
    double D
    Object Ljava/lang/Object;
    int[] [I
    Object[][] [[Ljava/lang/Object;

     在ASM中要获得一个类的JVM内部描述,可以使用org.objectweb.asm.Type类中的getDescriptor(final Class c)方法,如下:

    public class TypeDescriptors {  
        public static void main(String[] args) {  
            System.out.println(Type.getDescriptor(TypeDescriptors.class));  
            System.out.println(Type.getDescriptor(String.class));  
        }  
      
    }  

      运行结果:

    Lcom/sunchao/asm/TypeDescriptors;  
    Ljava/lang/String;  

     四、方法描述:

          在Java的二进制文件中,方法的方法名和方法的描述都是存储在Constant pool中的,且在两个不同的单元里。因此,方法描述中不含有方法名,只含有参数类型和返回类型,如下:

    方法描述,在类中的 方法描述,在二进制文件中的
    void a(int i,float f) (IF)V
    void a(Object o) (Ljava/lang/Object;)V
    int a(int i,String s) (ILjava/lang/String;)I
    int[] a(int[] i) ([I)[I
    String a() ()Ljava/lang/String;

         获取一个方法的描述可以使用org.objectweb.asm.Type.getMethodDescriptor方法,如下:

    public class MethodDescriptors {  
        public static void main(String[] args) throws Exception {  
            Method m = String.class.getMethod("substring", int.class);  
            System.out.println(Type.getMethodDescriptor(m));  
        }  
      
    }  

    运行结果:

    (I)Ljava/lang/String;  

    其实在org.objectweb.asm.Type类中提供了很多方法。

    原文请见:http://exceptioneye.iteye.com/category/253313

  • 相关阅读:
    插件模块与模块之间的通信(转)
    C#反射调用其它DLL的委托事件 传值
    单元测试
    c#实现动态加载Dll(转)
    Access sql语句创建表及字段类型(转)
    关于不同数据库表自动转换的功能
    通过DataTable获得表的主键
    C/s程序过时了吗?
    关于C/s结构 本地目录的思考
    关于创建人,创建日期,修改人,修改日期
  • 原文地址:https://www.cnblogs.com/onlysun/p/4533869.html
Copyright © 2020-2023  润新知