1. What is ASM?
ASM is a java bytecode control framework that can be used to dynamically generate classes or enhance the functions of existing classes. ASM can directly generate binary class files, or dynamically change the class behavior before the class is added to the Java Virtual Machine. Java class is stored in strictly formatted. in the class file, these class files have enough metadata to parse all elements in the class: class Name, method, attribute, and Java bytecode (instruction ). After ASM reads information from class files, it can change class behavior, analyze class information, and even generate new classes according to user requirements.
To use the ASM framework, you need to import the jar package, asm-3.2.jar of asm.
Ii. How to Use ASM
The core classes in the ASM framework are as follows:
① ClassReader: this class is used to parse compiled class bytecode files.
② ClassWriter: this class is used to re-build the compiled class, such as modifying the class name, attributes, and methods, and even generating bytecode files for the new class.
③ ClassAdapter: This class also implements the ClassVisitor interface, which delegates its method call to another ClassVisitor object.
Example 1. Use asm to generate the class bytecode
main(String[] args) ClassWriter cw = ClassWriter(0 cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT+ "com/asm3/Comparable", , "java/lang/Object", String[]{"com/asm3/Mesurable" cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+ "LESS", "I", , Integer(-1 cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+ "EQUAL", "I", , Integer(0 cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+ "GREATER", "I", , Integer(1 cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT, "compareTo" "(Ljava/lang/Object;)I", , cw.visitEnd(); [] data = File file = File("D://Comparable.class" FileOutputStream fout = }
To generate a class bytecode file, you only need to use the ClassWriter class to generate Comparable. class: javap-c Comparable. class> test.txt. The compiled result is as follows:
com.asm3.Comparable }
Example 2. Modify the class bytecode File
C. java
m() Thread.sleep(100 }
Change the C. java class content to the following:
m() timer -= Thread.sleep(100 timer += }
To figure out how ASM is implemented, we first compile these two classes and then compare their TraceClassVisitor output. We can find the following differences (in bold)
Getstatic c. timer: J
INVOKESTATIC java/lang/System. currentTimilis () J
LSUB
Putstatic c. timer: J
LTC 100
INVOKESTATIC java/lang/Thread. sleep (J) V
Getstatic c. timer: J
INVOKESTATIC java/lang/System. currentTimilis () J
LADD
Putstatic c. timer: J
RETURN
MAXSTACK = 4
MAXLOCALS = 1
By comparing the preceding commands, we can find that four commands must be added at the beginning of the m () method, and four commands must be added before the RETURN command, at the same time, these four commands must be placed before xRETURN and ATHROW, because these commands will end method execution.
The Code is as follows:
AddTimeClassAdapter. java
AddTimeClassAdapter visit( version, owner = isInterface = (access & Opcodes.ACC_INTERFACE) != 0 MethodVisitor visitMethod( MethodVisitor mv = (!name.equals("<init>") && !isInterface && mv!= mv = (! FieldVisitor fv = cv.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_STATIC, "timer", "J", , (fv!= AddTimeMethodAdapter mv.visitFieldInsn(Opcodes.GETSTATIC, owner, "timer", "J" mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J" mv.visitFieldInsn(Opcodes.PUTSTATIC, owner, "timer", "J" visitInsn( ((opcode>=Opcodes.IRETURN && opcode<=Opcodes.RETURN) || opcode== mv.visitFieldInsn(Opcodes.GETSTATIC, owner, "timer", "J" mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J" mv.visitFieldInsn(Opcodes.PUTSTATIC, owner, "timer", "J" visitMaxs( maxStack, mv.visitMaxs(maxStack+4 }
Generator. java
ClassReader cr = ClassReader("com/asm5/C" ClassWriter cw = ClassAdapter classAdapter = [] data = File file = File(System.getProperty("user.dir") + "\\WebRoot\\WEB-INF\\classes\\com\\asm5\\C.class" FileOutputStream fout = System.out.println("success!" } } }
Below is a test class:
main(String[] args) C c = Class cc = System.out.println(cc.getField("timer" }
Output result: 100