前言
很早之前就写过面向切面的编程思想,主要学习了AOP的思想(参考:AOP简介)以及使用 AspectJ 实现简单的切面编程(参考:AspectJ之切点语法)。
其他常见的AOP编程框架还有 Cglib,Hibernate 和 Spring 等等,而这些目前流行的AOP框架绝大多数底层实现都是直接或间接地通过 ASM 来实现字节码操作。
因此,如果你想实现一些简单的切面编程,直接采用上面提及的AOP框架是绝对可以实现的,但是这些框架相对于 ASM 来说重了许多,在你进行代码切入的时候,可能会为你引入许多其他包的代码,导致生成的class文件体积增大不少,因此,对于一些简单的代码切片,推荐使用 ASM 字节码操作库直接对class文件动态进行代码切入。
ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
简单的说,ASM 可以读取解析class文件内容,并提供接口让你可以对class文件字节码内容进行CRUD操作······
注: class文件存储的是java字节码,ASM 是对java字节码操作的一层封装,因此,如果你很了解 class文件格式的话,你甚至可以通过直接使用文本编辑器(eg:Vim)来改写class文件。
知道了 ASM 的作用后,接下来我们就来看下 ASM 的执行模式,了解它的执行模式后,我们才能更好地使用。
ASM 框架执行流程
ASM 提供了两组API:Core和Tree:
Core是基于访问者模式来操作类的
Tree是基于树节点来操作类的
本文我们主要讨论的是 ASM 的 CoreAPI。
ASM 内部采用 访问者模式 将 .class 类文件的内容从头到尾扫描一遍,每次扫描到类文件相应的内容时,都会调用ClassVisitor内部相应的方法。
比如:
扫描到类文件时,会回调ClassVisitor的visit()方法;
扫描到类注解时,会回调ClassVisitor的visitAnnotation()方法;
扫描到类成员时,会回调ClassVisitor的visitField()方法;
扫描到类方法时,会回调ClassVisitor的visitMethod()方法;
······
扫描到相应结构内容时,会回调相应方法,该方法会返回一个对应的字节码操作对象(比如,visitMethod()返回MethodVisitor实例),通过修改这个对象,就可以修改class文件相应结构部分内容,最后将这个ClassVisitor字节码内容覆盖原来.class文件就实现了类文件的代码切入。
具体关系如下:
树形关系
使用的接口
Class
ClassVisitor
Field
FieldVisitor
Method
MethodVisitor
Annotation
AnnotationVisitor
整个具体的执行时序如下图所示:
ASM执行流程时序图
通过时序图可以看出ASM在处理class文件的整个过程。ASM通过树这种数据结构来表示复杂的字节码结构,并利用 Push模型 来对树进行遍历。
ASM 中提供一个ClassReader类,这个类可以直接由字节数组或者class文件