Parsing classes
解析现有类的唯一必需组件是ClassReader组件。 让我们以一个例子来说明这一点。 假设我们要以类似于javap工具的方式打印类的内容。 第一步是编写ClassVisitor类的子类,以打印有关其访问的类的信息。 这是一个可能的,过于简化的实现:
import org.objectweb.asm.*;
import static org.objectweb.asm.Opcodes.ASM4;
public class ClassPrinter extends ClassVisitor {
public ClassPrinter() {
super(ASM4);
}
@Override
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) {
}
@Override
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) {
return null;
}
@Override
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消耗由ClassReader产生的事件:
import org.objectweb.asm.ClassReader;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
ClassPrinter cp = new ClassPrinter();
ClassReader cr = new ClassReader("java.lang.Runnable");
cr.accept(cp,0);
}
}
上面的代码 main() 方法中:
在第二行创建一个ClassReader来解析Runnable类。 java.lang.Runnable 源码如下:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
在最后一行调用的accept方法解析Runnable类字节码,并在cp上调用相应的ClassVisitor方法。
结果是以下输出:
java/lang/Runnable extends java/lang/Object {
run ()V
}
请注意,有几种方法可以构造ClassReader实例。 可以通过名称(如上所述)或通过值(字节数组或InputStream)指定必须读取的类。 可以使用ClassLoader的getResourceAsStream方法通过以下方式获取用于读取类内容的输入流:
import org.objectweb.asm.ClassReader;
import java.io.IOException;
import java.io.InputStream;
public class Test {
public static void main(String[] args) throws IOException {
ClassPrinter cp = new ClassPrinter();
// 方式一 构建ClassReader
ClassReader cr = new ClassReader("java.lang.Runnable");
cr.accept(cp,0);
System.out.println("====================================================");
InputStream ip = Test.class.getClassLoader().getResourceAsStream("java.lang.Runnable".replace(".", "/") + ".class");
// 方式二 构建ClassReader
ClassReader cr2 = new ClassReader(ip);
cr2.accept(cp,0);
}
}
如上面的方式二,我们通过使用ClassLoader的getResourceAsStream方法通过以下方式获取用于读取类内容的输入流,然后传给ClassReader。
运行结果为:
java/lang/Runnable extends java/lang/Object {
run ()V
}
====================================================
java/lang/Runnable extends java/lang/Object {
run ()V
}
方式一和方式二 都可以正常读取字节码。