美文网首页
09 - ASM使用ClassWrite生成接口+字段+方法

09 - ASM使用ClassWrite生成接口+字段+方法

作者: 舍是境界 | 来源:发表于2022-01-18 07:08 被阅读0次

    生成接口+字段+方法

    目标
    public interface HelloWorld extends Cloneable {
        int LESS = -1;
        int EQUAL = 0;
        int GREATER = 1;
        int compareTo(Object o);
    }
    
    编码实现
    import lsieun.utils.FileUtils;
    import org.objectweb.asm.ClassWriter;
    import org.objectweb.asm.FieldVisitor;
    import org.objectweb.asm.MethodVisitor;
    
    import static org.objectweb.asm.Opcodes.*;
    
    public class HelloWorldGenerateCore {
        public static void main(String[] args) throws Exception {
            String relative_path = "sample/HelloWorld.class";
            String filepath = FileUtils.getFilePath(relative_path);
    
            // (1) 生成byte[]内容
            byte[] bytes = dump();
    
            // (2) 保存byte[]到文件
            FileUtils.writeBytes(filepath, bytes);
        }
    
        public static byte[] dump() throws Exception {
            // (1) 创建ClassWriter对象
            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
    
            // (2) 调用visitXxx()方法
            cw.visit(V1_8, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, "sample/HelloWorld",
                    null, "java/lang/Object", new String[]{"java/lang/Cloneable"});
    
            {
                FieldVisitor fv1 = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "LESS", "I", null, -1);
                fv1.visitEnd();
            }
    
            {
                FieldVisitor fv2 = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "EQUAL", "I", null, 0);
                fv2.visitEnd();
            }
    
            {
                FieldVisitor fv3 = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "GREATER", "I", null, 1);
                fv3.visitEnd();
            }
    
            {
                MethodVisitor mv1 = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "compareTo", "(Ljava/lang/Object;)I", null, null);
                mv1.visitEnd();
            }
    
    
            cw.visitEnd();
    
            // (3) 调用toByteArray()方法
            return cw.toByteArray();
        }
    }
    

    在上述代码中,我们调用了visit()方法、visitField()方法、visitMethod()方法、visitEnd()方法和toByteArray()方法。

    结果验证
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    public class HelloWorldRun {
        public static void main(String[] args) throws Exception {
            Class<?> clazz = Class.forName("sample.HelloWorld");
            Field[] declaredFields = clazz.getDeclaredFields();
            if (declaredFields.length > 0) {
                System.out.println("fields:");
                for (Field f : declaredFields) {
                    System.out.println("    " + f.getName());
                }
            }
    
            Method[] declaredMethods = clazz.getDeclaredMethods();
            if (declaredMethods.length > 0) {
                System.out.println("methods:");
                for (Method m : declaredMethods) {
                    System.out.println("    " + m.getName());
                }
            }
        }
    }
    
    fields:
        LESS
        EQUAL
        GREATER
    methods:
        compareTo
    

    visitField()和visitMethod()方法

    visitField(access, name, descriptor, signature, value)
    visitMethod(access, name, descriptor, signature, exceptions)
    

    这两个方法的前4个参数是相同的,不同的地方只在于第5个参数。

    • access参数:表示当前字段或方法带有的访问标识(access flag)信息,例如ACC_PUBLIC、ACC_STATIC和ACC_FINAL等。
    • name参数:表示当前字段或方法的名字。
    • descriptor参数:表示当前字段或方法的描述符。这些描述符,与我们平时使用的Java类型是有区别的。
    • signature参数:表示当前字段或方法是否带有泛型信息。换句话说,如果不带有泛型信息,提供一个null就可以了;如果带有泛型信息,就需要给它提供某一个具体的值。
    • value参数:是visitField()方法的第5个参数。这个参数的取值,与当前字段是否为常量有关系。如果当前字段是一个常量,就需要给value参数提供某一个具体的值;如果当前字段不是常量,那么使用null就可以了。
    • exceptions参数:是visitMethod()方法的第5个参数。这个参数的取值,与当前方法声明中是否具有throws XxxException相关。

    描述符(descriptor)

    在ClassFile当中,描述符(descriptor)是对“类型”的简单化描述。

    • 对于字段(field)来说,描述符就是对字段本身的类型进行简单化描述。
    • 对于方法(method)来说,描述符就是对方法的接收参数的类型和返回值的类型进行简单化描述。
    描述符示意图
    • 对字段描述符的举例:
      • boolean flag: Z
      • byte byteValue: B
      • int intValue: I
      • float floatValue: F
      • double doubleValue: D
      • String strValue: Ljava/lang/String;
      • Object objValue: Ljava/lang/Object;
      • byte[] bytes: [B
      • String[] array: [Ljava/lang/String;
      • Object[][] twoDimArray: [[Ljava/lang/Object;
    • 对方法描述符的举例:
      • int add(int a, int b): (II)I
      • void test(int a, int b): (II)V
      • boolean compare(Object obj): (Ljava/lang/Object;)Z
      • void main(String[] args): ([Ljava/lang/String;)V

    相关文章

      网友评论

          本文标题:09 - ASM使用ClassWrite生成接口+字段+方法

          本文链接:https://www.haomeiwen.com/subject/wquvcrtx.html