一:ASM概述
ASM是一个Java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM可以直接产生二进制class文件,也可以在类被加载入Java虚拟机之前动态改变类行为,ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。目前许多框架如cglib、Hibernate、Spring都直接或间接地使用ASM操作字节码。
二:ASM的Core API
Core API 中操纵字节码的功能基于 ClassVisitor 接口。这个接口中的每个方法对应了 class文件中的每一项。ASM 提供了三个基于 ClassVisitor 接口的类来实现 class 文件的生成和转换:
1:ClassReader:ClassReader 解析一个类的 class 字节码,该类的 accept 方法接受一个 ClassVisitor的对象,在 accept 方法中,会按上文描述的顺序逐个调用 ClassVisitor 对象的方法。
2:ClassAdapter:ClassAdapter 是 ClassVisitor 的实现类。它的构造方法中需要一个 ClassVisitor 对象,并保存为字段 protected ClassVisitor cv。在它的实现中,每个方法都是原封不动的直接调用cv 的对应方法,并传递同样的参数。
3:ClassWriter:ClassWriter 也是 ClassVisitor 的实现类。ClassWriter 可以用来以二进制的方式创建一个类的字节码。可以通过 toByteArray 方法获取生成的字节数组。
三:ASM的ASMifer工具
ASM给我们提供了ASMifer工具来帮助开发,可使用ASMifer工具生成ASM结构来对比,比如使用命令:
java -cp .;asm-all-5.2.jar org.objectweb.asm.util.ASMifier Test (其中Test是编译后的class文件名)
四:ASM实战(通过ASM为类的所有方法添加一个耗时计算的功能)
- 创建一个类CC.java 复制代码1
2
3
4
5
6
7
8
9
10
11
12package com.yuy.jvm; public class CC { public void m() throws InterruptedException { System.out.println("now in CC.m"); Thread.sleep(100L); } }
2.通过java -cp 查看其编译的class文件编码(java -cp .;asm-all-5.2.jar org.objectweb.asm.util.ASMifier CC)
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
40package asm.com.yuy.jvm; import java.util.*; import org.objectweb.asm.*; public class CCDump implements Opcodes { public static byte[] dump () throws Exception { ClassWriter cw = new ClassWriter(0); FieldVisitor fv; MethodVisitor mv; AnnotationVisitor av0; cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, "com/yuy/jvm/CC", null, "java/lang/Object", null); { mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); mv.visitInsn(RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } { mv = cw.visitMethod(ACC_PUBLIC, "m", "()V", null, new String[] { "java/lang/InterruptedException" }); mv.visitCode(); mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitLdcInsn("now in CC.m"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); mv.visitLdcInsn(new Long(100L)); mv.visitMethodInsn(INVOKESTATIC, "java/lang/Thread", "sleep", "(J)V", false); mv.visitInsn(RETURN); mv.visitMaxs(2, 1); mv.visitEnd(); } cw.visitEnd(); return cw.toByteArray(); } }
3.修改CC.java类,(修改只是为了查看加上计时器的class编码,拷贝到编码之后需要将CC还原)如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14package com.yuy.jvm; public class CC { public void m() throws InterruptedException { long startTime = System.currentTimeMillis(); System.out.println("now in CC.m"); Thread.sleep(100L); long endTime = System.currentTimeMillis() - startTime; System.out.println("方法耗时为:"+endTime ); } }
4.通过java -cp 查看修改之后的class文件编码
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
56package asm.com.yuy.jvm; import java.util.*; import org.objectweb.asm.*; public class CCDump implements Opcodes { public static byte[] dump () throws Exception { ClassWriter cw = new ClassWriter(0); FieldVisitor fv; MethodVisitor mv; AnnotationVisitor av0; cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, "com/yuy/jvm/CC", null, "java/lang/Object", null); { mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); mv.visitInsn(RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } { mv = cw.visitMethod(ACC_PUBLIC, "m", "()V", null, new String[] { "java/lang/InterruptedException" }); mv.visitCode(); mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false); mv.visitVarInsn(LSTORE, 1); mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitLdcInsn("now in CC.m"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); mv.visitLdcInsn(new Long(100L)); mv.visitMethodInsn(INVOKESTATIC, "java/lang/Thread", "sleep", "(J)V", false); mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false); mv.visitVarInsn(LLOAD, 1); mv.visitInsn(LSUB); mv.visitVarInsn(LSTORE, 3); mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false); mv.visitLdcInsn("u65b9u6cd5u8017u65f6u4e3auff1a"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); mv.visitVarInsn(LLOAD, 3); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;", false); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); mv.visitInsn(RETURN); mv.visitMaxs(4, 5); mv.visitEnd(); } cw.visitEnd(); return cw.toByteArray(); } }
5.对比修改前后的class文件编码:
6.创建MyClassVisitor类:
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
64package com.yuy.jvm.asm; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; public class MyClassVisitor extends ClassVisitor { public MyClassVisitor(ClassVisitor cv){ super(Opcodes.ASM5,cv); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { cv.visit(version,access,name,signature,superName,interfaces); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = cv.visitMethod(access,name,desc,signature,exceptions); //字节码中的init为构造方法 if(!name.equals("<init>") && mv != null){ //为方法增加计时的功能 mv = new MyMethodVisitor(mv); } return mv; } class MyMethodVisitor extends MethodVisitor{ public MyMethodVisitor(MethodVisitor mv){ super(Opcodes.ASM5,mv); } @Override public void visitCode() { super.visitCode(); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false); mv.visitVarInsn(Opcodes.LSTORE, 1); } @Override public void visitInsn(int opcode) { if(opcode >= Opcodes.IRETURN && opcode <=Opcodes.RETURN || opcode ==Opcodes.ATHROW){ mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false); mv.visitVarInsn(Opcodes.LLOAD, 1); mv.visitInsn(Opcodes.LSUB); mv.visitVarInsn(Opcodes.LSTORE, 3); mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder"); mv.visitInsn(Opcodes.DUP); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false); mv.visitLdcInsn("u65b9u6cd5u8017u65f6u4e3auff1a"); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); mv.visitVarInsn(Opcodes.LLOAD, 3); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;", false); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); } mv.visitInsn(opcode); } } }
7.创建Generator
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
28package com.yuy.jvm.asm; import com.sun.org.apache.xpath.internal.SourceTree; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; public class Generator { public static void main(String[] args) throws IOException { ClassReader cr = new ClassReader("com/yuy/jvm/CC"); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassVisitor cv = new MyClassVisitor(cw); cr.accept(cv,ClassReader.SKIP_DEBUG); byte[]data = cw.toByteArray(); //输出 File f = new File("out/production/JVMTest/com/yuy/jvm/CC.class"); FileOutputStream fout = new FileOutputStream(f); fout.write(data); fout.close(); System.out.println("now generator cc success"); } }
8.执行Genrator中的main方法,改写CC.calss
9.执行test测试
1
2
3
4
5
6
7
8
9
10
11package com.yuy.jvm.asm; import com.yuy.jvm.CC; public class MyTest { public static void main(String[] args) throws InterruptedException { CC cc = new CC(); cc.m(); } }
10.说明:a.修改字节码之前执行Mytest控制台输出。
now in CC.m
b.执行Generator .main方法修改class文件之后执行Mytest控制台输出
now in CC.m
方法耗时为:101
CC.java 里面是没有方法耗时的代码 的,但是CC.class 经过重新Generator重新改造之后将统计耗时方法的字节码 代 码添加到CC.class文件中,所以会有统计方法耗时的输入会打印出来。
c.为了验证是统计所有方法的执行耗时。所以在CC.java中复制方法m 改名a b执行调a方法或b方法均有耗时打印
说明是支持的CC类的所有方法的耗时统计。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25package com.yuy.jvm; public class CC { public void m() throws InterruptedException { System.out.println("now in CC.m"); Thread.sleep(100L); } public void a() throws InterruptedException { System.out.println("now in CC.m"); Thread.sleep(100L); } public void b() throws InterruptedException { System.out.println("now in CC.m"); Thread.sleep(100L); } }
最后
以上就是老迟到黑裤最近收集整理的关于JVM学习笔记3:字节码操纵框架ASM的全部内容,更多相关JVM学习笔记3内容请搜索靠谱客的其他文章。
发表评论 取消回复