ASM

https://blog.csdn.net/lijingyao8206/article/details/46430403
https://blog.csdn.net/lijingyao8206/article/details/46483815
https://blog.csdn.net/lijingyao8206/article/details/46715409

ASM是一个提供字节码解析和操作的框架。可以用于解析和生成字节码。

解析
要解析的类

1
2
3
4
5
6
7
public class Programmer {

public void code() {
System.out.println("I'm a Programmer,Just Coding.....");
}

}

ClassPrintVisitor 类继承自ClassVisitor类来打印解析类的类名,父类名以及“is”开头的属性和方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ClassPrintVisitor extends ClassVisitor {

public ClassPrintVisitor() {
super(Opcodes.ASM7);
}

public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
System.out.println(name + " extends " + superName + " {");
}

public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value) {
if (name.startsWith("name")) {
System.out.println(" field: " + name + " " + desc);
}
return null;
}

public MethodVisitor visitMethod(int access, String name,
String desc, String signature, String[] exceptions) {
System.out.println(" method: " + name + " " + desc);
return null;
}

public void visitEnd() {
System.out.println("}");
}
}

下面是测试类ClassesPrintTest 。将一个ClassPrintVisitor 对象传给ClassReader。ClassReader作为一个解析事件的producer 并且由ClassPrintVisitor去消费(处理打印逻辑)。accept()方法就将Task 字节码进行解析,然后调用ClassPrintVisitor 的方法。 

1
2
3
4
public static void visit() throws Exception {
ClassReader cr = new ClassReader("com.zero.jdk_cglib.asm.Programmer");
cr.accept(new ClassPrintVisitor(), 0);
}

运行结果:

1
2
3
4
5
com/zero/jdk_cglib/asm/Programmer extends java/lang/Object {
field: name Ljava/lang/String;
method: <init> ()V
method: code ()V
}

生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public class AsmGenerator {

public static void main(String[] args) throws Exception {
Class clazz = new ClassLoader() {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] b = classBytes();
return defineClass(name, b, 0, b.length);
}
}.findClass(null);

//测试加载是否成功,打印class 对象的名称
System.out.println(clazz.getCanonicalName());

//实例化一个Programmer对象
Object o = clazz.newInstance();
clazz.getMethod("code", null).invoke(o, null);
}

private static byte[] classBytes() {
ClassWriter classWriter = new ClassWriter(0);
// 通过visit方法确定类的头部信息
classWriter.visit(Opcodes.V1_8,// java版本
Opcodes.ACC_PUBLIC,// 类修饰符
"Programmer", // 类的全限定名
null, "java/lang/Object", null);

//创建构造函数
MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();

// 定义code方法
MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "code", "()V",
null, null);
methodVisitor.visitCode();
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out",
"Ljava/io/PrintStream;");
methodVisitor.visitLdcInsn("I'm a Programmer,Just Coding.....");
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
"(Ljava/lang/String;)V");
methodVisitor.visitInsn(Opcodes.RETURN);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();
classWriter.visitEnd();
// 使classWriter类已经完成
// 将classWriter转换成字节数组写到文件里面去
byte[] data = classWriter.toByteArray();
return data;
}
/**
* public class Programmer {
* public void code() {
* System.out.println("I'm a Programmer,Just Coding.....");
* }
* }
*/

}

   ASM的Core API 中给我们提供了一些工具类,都在 org.objectweb.asm.util包中。有TraceClassVisitor、CheckClassAdapter、ASMifier、Type等。通过这些工具类,能更方便实现我们的动态生成字节码逻辑。这里就简述一下TraceClassVisitor 。

   TraceClassVisitor 顾名思义,我们可以“trace”也就是打印一些信息,这些信息就是ClassWriter 提供给我们的byte字节数组。因为我们阅读一个二进制字节流还是比较难以理解和解析一个类文件的结构。TraceClassVisitor通过初始化一个classWriter 和一个Printer对象,来实现打印我们需要的字节流信息。通过TraceClassVisitor 我们能更好地比较两个类文件,更轻松得分析class的数据结构。

  下面看个例子,我们用TraceClassVisitor 来打印Task 类信息。