ASM指南翻译-3

2.1接口和组件

 

2.2.1表现Presentation

生成和转换编译后的类的ASM API是基于ClassVisitor接口的(见图2.4)。在这个接口中的每一个方法都与类文件中有着相同名称的段相对应(见图2.1)。在访问类结构中简单的段时,是通过调用一个独立的方法来实现的,该方法的参数就是该段相关的内容,该方法的返回值为void。对长度任意并且较复杂的段进行访问时,是通过一个初始化方法返回一个辅助的visitor接口来实现,例如visitAnnotationvisitField以及visitMethod,它们都返回与之对应的接口AnnotationVisitorFieldVisitor以及MethodVisitor

 

这些规则也同样适用于这些辅助接口。例如,在FieldVisitor接口中的每个方法,都与类文件结构中与该名称(Field)对应的子结构对应(见图2.5),并且visitAnnotation并会一个辅助的AnnotationVisitor接口,与ClassVisitor中的AnnotationVisitor相同。关于这些辅助接口的创建和使用,将在下一章节介绍,这一章主要限于那些简单的问题,使用ClassVisitor接口就可以解决的。

ASM指南翻译-3_第1张图片

2.4 ClassVisitor接口

ASM指南翻译-3_第2张图片

2.5 FieldVisitor接口

ClassVisitor接口中方法的调用必须遵循下面文档定义的顺序,该文档定义在ClassVisitor接口的Javadoc中。

visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )*( visitInnerClass | visitField | visitMethod )*

visitEnd

 

这就意味着visit必须被第一个调用,然后调用visitSource方法,最多调用一次,再接着是visitOuterClass,然后再调用任意次数的visitAnnotation或者visitAttribute方法,接着可以调用任意次数的visitInnerClassvisitiField或者visitMethod,顺序不限,在最后,调用visitEnd方法。

ClassVisitor接口的基础上,ASM提供了三个组件来生成和转换类:

  • ClassReader 用来解析编译过的class的字节数组。然后,调用ClassVisitor实例的visitXxx方法,其中ClassVisitor实例作为ClassReader.accept方法的参数传递进去的。ClassReader可以被看做是一个事件产生者。
  • ClassWriterClassVisitor接口的一个实现,用来以二进制方式构建编译后的类。它产生一个包含编译后的类的字节数组,可以通过它的toByteArray方法来或得。它可以被看做是一个事件消费者。
  • ClassAdapter也是ClassVisitor接口的一个实现,它将对它的方法调用委托给另一个ClassVisitor。它可以被认为是一个事件过滤器。

接下来,将结合具体的例子来展示如何使用这些组件来生成和转换类。

 

2.2.2解析类

解析一个已存在的类仅需要ClassReader这个组件。下面让我们以一个实例来展示如何解析类。假设,我们想要打印一个类的内容,我们可以使用javap这个工具。第一步,实现ClassVisitor这个接口,用来打印类的信息。下面是一个简单的实现:

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("}");

         }

}

第二步,将ClassPrinterClassReader结合起来,这样,ClassReader产生的事件就可以被我们的ClassPrinter消费了:

ClassPrinter cp = new ClassPrinter();

ClassReader cr = new ClassReader("java.lang.Runnable");

cr.accept(cp, 0);

 

上面的第二行代码创建了一个ClassReader来解析Runnable类。最后一行代码中的accept方法解析Runnable类的字节码,并且调用cp上对应的方法。结果如下:

java/lang/Runnable extends java/lang/Object {

run()V

}

 

注意,这里有多种方式来构造一个ClassReader的实例。可以通过类名,例如上面的例子,或者通过类的字节数组。或者类的输入流。类的输入流可以通过ClassLoadergetResourceAdStream方法:

cl.getResourceAsStream(classname.replace(’.’, ’/’) + ".class");

你可能感兴趣的:(ASM)