ASM 是一个 Java 字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
1 | <dependency> |
主要类
ClassReader
用于读取解析字节码文件1
ClassReader cr = new ClassReader(Test.class.getName());
ClassWriter
用于生产一个新的字节码文件,继承ClassVisitor。1
ClassWriter cw = new ClassWriter(0);
ClassAdapter
ClassAdapter继承ClassVisitor。构造一个ClassAdapter需要传入一个ClassWriter。这是装饰模式。对ClassWirter的增强。1
ClassAdapter ca= new ClassAdapter(cw);
ClassAdapter可以被一个ClassReader接受,将ClassReader产生的事件传递给ClassWriter。这样方便基于ClassReader读取的class上设置新的class文件。默认的ClassAdapter会完全将ClassReader复制到ClassWriter,所以一般都会根据实现ClassAdapter的自定义子类。1
2
3
4ClassReader cr = new ClassReader(Test.class.getName());
ClassWriter cw = new ClassWriter(0);
ClassAdapter ca= new ClassAdapter(cw);
cr.accept(ca,0);
ClassVisitor
ClassVisitor的接口API。ClassAdapter和ClassWriter都继承ClassVisitor,他们的API都一样。
ClassAdapter的对应方法参数都是从ClassReader中读取的。ClassWriter通过设置对应方法这些参数生成类。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public abstract class ClassVisitor {
public ClassVisitor(int api);
public ClassVisitor(int api, ClassVisitor cv);
//类头(类版本号,访问控制标识,类名,类签名(包括泛型),父类名,接口)
public void visit(int version, int access, String name, String signture, String superName, String[] interfaces);
//源文件
public void visitSource(String source, String debug);
//
public void visitOuterClass(String owner, String name, String desc);
//注解
AnnotationVisitor visitAnnotation(String desc, boolean visible);
//属性
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);
//方法
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions);
//用于添加属性
void visitEnd();
}
使用例子
在Bean类加一个字段,并生成class文件
1 | public class Bean { |
1 | //1.读取Bean类 |
添加方法
这个添加一个无参数构造方法的实现。1
2
3
4
5
6
7
8
9MethodVisitor mv = classWriter.visitMethod(1, "<init>", "()V", null, null);
//添加方法methodVisitor需要执行下面的方法,生成方法体
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
//第二个参数是父类名,这里是调用父类的构造函数
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
将classWriter转成Class
调用classLoader的defineClass方法将字节数组转化为Class,defineClass方法是protected方法,所以要继承ClassLoader才可以使用。1
2byte[] bytes = classWriter.toByteArray();
return defineClass(className, bytes, 0, bytes.length);