引入maven依赖
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.2</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
<version>9.2</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
<version>9.2</version>
</dependency>
这里暂时没有仔细研究关于各个依赖具体的作用,但是保证引入之后能使用ASM提供的api操纵字节码
ASM提供的基本组件
在ASM所提供的字节码操控框架中,字节码作为数据在框架中的流向遵循如下模型
reader -》 visitor -》 writer
如上提及的三个组件,也是ASM框架中最基本的三个组件
- ClassReader 将编译后的字节码文件以byte数组的形式读取出来,并且作为事件的生产者。ClassReader在accept方法中接收一个ClassVisitor实例,并调用其中对应的VisitXxx方法。
- ClassWriter ClassVisitor的一个实现类,以二进制的方式直接构建字节码文件,可以通过调用toByteArray方法获取含有编译后文件的byte数组,同时也是事件的消费者
- ClassVisitor 类似一个事件的过滤器,其所有的方法调用都会委托给自身持有的另一个ClassVisitor实例。
组件之间以事件驱动,具体如何五花八门的使用这些组件我们后续再详细说明,现在我们主要来介绍java的字节码文件。但是原味的字节码不适合人类直接阅读,所以第一步先来以可读性更高的方式来阅读字节码文件。
在asm-util中也提供了内置的visitor实现,这里演示如何使用TraceClassVisitor
将字节码文件以可读性更高(偏向于汇编)的方式输出到文件中
public void showClassInTextual(String className, String pathToFile) {
try {
ClassReader cr = new ClassReader(className);
ClassWriter cw = new ClassWriter(0);
int pos = className.lastIndexOf('.');
String fileName = className.substring(pos+1);
File file = new File(pathToFile + File.separator + fileName + ".txt");
OutputStream outputStream = new FileOutputStream(file);
TraceClassVisitor visitor = new TraceClassVisitor(cw, new PrintWriter(outputStream));
cr.accept(visitor, 0);
System.out.println("finish");
} catch (Exception e) {
e.printStackTrace();
}
}
在前文中我们提到过,writer会根据reader传入的事件进行响应。但是在这个示例中,我们并没有对字节码内容的进行改动,最理想的情况是reader将每一部分的byte直接复制传递,不需要额外解析。实际上ASM也确实有针对这部分的优化
If a ClassReader component detects that a MethodVisitor returned by the ClassVisitor passed as argument to its accept method comes from a ClassWriter,this means that the content of this method will not be transformed, and will in fact not even be seen by the application
In this case the ClassReader component does not parse the content ofthis method, does not generate the corresponding events, and just copies,the byte array representation of this method in the ClassWriter.
如果ClassReader检测到通过accept方法接收的ClassVisitor返回的MethodVisitor方法是来自于ClassWriter,那么说明这部分内容没有改动过。在这种情况下,ClassReader不会解析内容也不会生成对应的事件,仅仅将byte array复制给ClassWriter
有了这个方法就可以看到编译后的代码了,显然源码和字节码文件是有很大区别的,在下一节我们将了解如何阅读编译后的字节码文件。
参考文献: