Core API之Class接口和组件

Presentation

    ASM生成和转换编译后class的API是基于ClassVisitor接口。简单部分通过简单方法调用(参数描述它们的内容,且返回类型为void)来访问。哪些内容长度不定且复杂的部分可以通过初始方法调用来访问,它返回附加的访问接口。visitAnnotation,visitField和visitMethod方法各自返回AnnotationVisitor、FieldVisitor和MethodVisitor。
public interface ClassVisitor {
    void visit(int version, int access, String name, String signature,
               String superName, String[] interfaces);
    void visitSource(String source, String debug);
    void visitOuterClass(String owner, String name, String desc);
               AnnotationVisitor visitAnnotation(String desc, boolean visible);
    void visitAttribute(Attribute attr);
    void visitInnerClass(String name, String outerName, String innerName,int access);
    FieldVisitor visitField(int access, String name, String desc,
         String signature, Object value);
    MethodVisitor visitMethod(int access, String name, String desc,
         String signature, String[] exceptions);
    void visitEnd();
}

public interface FieldVisitor {
    AnnotationVisitor visitAnnotation(String desc, boolean visible);
    void visitAttribute(Attribute attr);
    void visitEnd();
}

    ClassVisitor接口必须按以下顺序调用:visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )* ( visitInnerClass | visitField | visitMethod )* visitEnd

    意味着,visit必须第一个被调用,紧接着最多调用一次visitSource,接着最多调用一次visitOuterClass,接着顺序调用多次visitAnnotation和visitAttribute,接着多次顺序调用visitInnerClass,visitFieldvisitMethod,调用最后visitEnd结束。ASM提供了三个基于ClassVisitor接口的核心组件来生成和转换class。

    ASM提供了三个基于ClassVisitor接口的核心组件来生成和转换class。

    CLassReader:将编译过的class转换为字节数组,且调用ClassVisitor实例的visitXxx方法,ClassVisitor作为参数传递给accept方法。可以看作是事件生产者。

    ClassWriter:ClassVisitor接口的一个实现,它以二进制格式直接构建编译后的class。它产生包含编译后的class的二进制字节数组作为输出。可以看作是事件消费者。

    ClassAdapter:ClassVisitor接口的一个实现,它委托所有来自另外一个ClassVisitor实例的方法调用。它可以看作是过滤器。

Parsing classes

    转换一个已存在 的class需要唯一 组件是 ClassReader。假设我们要打印class的内容,类似反编译命令javap。第一步实现ClassVisitor接口来打印class信息。
public class ClassPrinter implements ClassVisitor {
    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("}");
    }
}
    第二步Classprinter与ClassReader一起使用,以便ClassReader产生的事件能被ClassPrinter来消费。    

ClassPrinter cp = new ClassPrinter();
ClassReader cr = new ClassReader("java.lang.Runnable");
cr.accept(cp, 0);
   accept方法在Runnable类字节码最后一行被转换后被调用,并且会调用cp上的ClassVisitor方法。  

Generating classes

生成Class的唯一组件是ClassWriter。
package pkg;
public interface Comparable extends Mesurable {
   int LESS = -1;
   int EQUAL = 0;
   int GREATER = 1;
   int compareTo(Object o);
}
ClassWriter cw = new ClassWriter(0);
cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,
     "pkg/Comparable", null, "java/lang/Object",
     new String[] { "pkg/Mesurable" });
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();
cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "compareTo",
     "(Ljava/lang/Object;)I", null, null).visitEnd();
cw.visitEnd();
byte[] b = cw.toByteArray();
    visit方法定义了class的头信息。V1_5是在ASM Opcodes接口中定义的常量,它指定了class的版本。ACC_XXX常量对应Java修饰符。这里我们指定 它是public和abstract。接下来 参数指明了类名、接口和父类。最后一个参数 它扩增的接口数组。
    连续的调用visitField方法,用来定义三个接口域,第一个参数是修饰符,第二个参数是field的名称,第三个参数是field类型。第四个参数泛型,在这里没有使用泛型,故为null。最后一个参数是field的常量值,这个参数仅仅被常量域使用。这里没有annotation,调用visitEnd方法。 visitMethod调用被用来定义compareTo方法。第一个参数是修饰符,第二个是方法名称。第三个参数是方法的描述。第四个为泛型,最后一个参数为方法能抛出的异常数组。visitMethod方法返回MethodVisitor,用来定义方法的annotation、属性和最为重要的方法的代码。接着调用visitEnd方法返回MethodVisitor。
最后一次调用visitEnd用来通知cw类已经结束,调用toByteArray获取字节数组。
可以使用自定义ClassLoader来加载生成的class。

class MyClassLoader extends ClassLoader {
    public Class defineClass(String name, byte[] b) {
        return defineClass(name, b, 0, b.length);
    }
}

Transforming classes 

 目前为止,ClassReader和ClassWriter组件被单独使用。联合使用这两个组件:

    1)ClassReader生产的event直接传递给ClassWriter。

byte[] b1 = ...;
ClassWriter cw = new ClassWriter();
ClassReader cr = new ClassReader(b1);
cr.accept(cw, 0);
byte[] b2 = cw.toByteArray();//b2代表了与b1Class一样的类.

    2)在ClassReader和ClassWriter引入ClassAdapter
byte[] b1 = ...;
ClasssWriter cw = new ClassWriter();
ClassAdapter ca = new ClassAdapter(cw); // ca forwards all events to cw
ClassReader cr = new ClassReader(b1);
cr.accept(ca, 0);
byte[] b2 = cw.toByteArray(); // b2 represents the same class as b1
转换链:ClassReader->ClassAdapter->ClassWriter
public class ChangeVersionAdapter extends ClassAdapter {
    public ChangeVersionAdapter(ClassVisitor cv) {
      super(cv);
    }
    @Override
    public void visit(int version, int access, String name,
        String signature, String superName, String[] interfaces) {
      cv.visit(V1_5, access, name, signature, superName, interfaces);
    }
}
通过修改visit方法的其他参数,你可以完成其他转换。

Using transformed classes

    转换过的class可以存储到磁盘上或使用ClassLoader加载,但是在一个ClassLoader内的class转换只能转换被该类的classLoader加载进来的类。如果你向转换所有class,你必须将转换置于ClassFileTransformer(定义在java.lang.instrument)内。

public static void premain(String agentArgs, Instrumentation inst) {
    inst.addTransformer(new ClassFileTransformer() {
       public byte[] transform(ClassLoader l, String name, Class c,
                  ProtectionDomain d, byte[] b)
       throws IllegalClassFormatException {
                  ClassReader cr = new ClassReader(b);
                  ClassWriter cw = new ClassWriter(cr, 0);
                  ClassVisitor cv = new ChangeVersionAdapter(cw);
                  cr.accept(cv, 0);
                 return cw.toByteArray();
       }
   });
}

Removing class members

    通过改变visitField和visitMethod方法中的access和name参数,就可以改变数据域和方法的访问修饰符或名字。这样作的效果就是数据域或方法被移除了。
本例子就是移除外部类、内部类和源文件名称。
public class RemoveDebugAdapter extends ClassAdapter {
    public RemoveDebugAdapter(ClassVisitor cv) {
       super(cv);
    }
    @Override
    public void visitSource(String source, String debug) {
    }
    @Override
    public void visitOuterClass(String owner, String name, String desc) {
    }
    @Override
    public void visitInnerClass(String name, String outerName,
           String innerName, int access) {
    }
}
下例移除了指定方法
public class RemoveMethodAdapter extends ClassAdapter {
    private String mName;
    private String mDesc;
    public RemoveMethodAdapter(
       ClassVisitor cv, String mName, String mDesc) {
          super(cv);
          this.mName = mName;
          this.mDesc = mDesc;
    }
    @Override
    public MethodVisitor visitMethod(int access, String name,
           String desc, String signature, String[] exceptions) {
       if (name.equals(mName) && desc.equals(mDesc)) {
       // do not delegate to next visitor -> this removes the method
           return null;
       }
       return cv.visitMethod(access, name, desc, signature, exceptions);
    }
}

Adding class members

public class AddFieldAdapter extends ClassAdapter {
    private int fAcc;
    private String fName;
    private String fDesc;
    private boolean isFieldPresent;
    public AddFieldAdapter(ClassVisitor cv, int fAcc, String fName,
           String fDesc) {
        super(cv);
        this.fAcc = fAcc;
        this.fName = fName;
        this.fDesc = fDesc;
    }
    @Override
    public FieldVisitor visitField(int access, String name, String desc,
           String signature, Object value) {
        if (name.equals(fName)) {
           isFieldPresent = true;
        }
        return cv.visitField(access, name, desc, signature, value);
    }
    @Override
    public void visitEnd() {
        if (!isFieldPresent) {
           FieldVisitor fv = cv.visitField(fAcc, fName, fDesc, null, null);
           if (fv != null) {
             fv.visitEnd();
           }
        }
        cv.visitEnd();
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值