ASM bytecode enhancement of

1. bytecode

1.1 What is bytecode

The reason why Java could "compile once, run anywhere", first, because the JVM for a variety of platforms and operating systems have been tailored for developers shielding the underlying details. Second, because both generate bytecode fixed format will compile on any platform (.class) files for use JVM, JVM virtual machines on different platforms can be loaded and executed the same and platform-independent bytecode. This shows the importance of Java byte code ecology. It is known as bytecode, the bytecode file because the composition hexadecimal value, the JVM bytes read. Generally used in Java javac over the source code bytecode files compiled from a sample .java files to run as shown.

java runtime schematic

1.2 Structure bytecode

.java files .class files obtained after javac compiler, such as the preparation of a simple class ByteCodeDemo, left portion of the figure below. After ByteCodeDemo.class compiled file, open a bunch hexadecimal number, divided in units of bytes as shown below shows the right portion shows.

Sample code and the corresponding byte code

JVM specification requires that each file required by the bytecode portion 10 composed of a fixed order, the overall results are shown in FIG.

Byte code file structure

2. ASM

ASM is a Java bytecode manipulation framework. It can be used to dynamically generate the class or classes of existing enhancements. ASM can have a direct binary class files can also be loaded into the Java virtual machine in the class before the class to change behavior. ASM has application scenario AOP (cglib based ASM), hot deploy, modify other jar package like. ASM bytecode modification process as shown in FIG.

ASM modified bytecode

2.1 ASM core API

2.1.1 ClassReader

For reading has been compiled .class files;

2.1.2 ClassWriter

It used to reconstruct the compiled classes, such as modifying the class name, methods and properties;

2.1.3 ClassVistor

ASM uses internal visitor mode according to the processing order from the top byte code, byte code files for different regions have different Visitor.

  • When accessing class file, callback ClassVistor the visit methods;
  • When accessing class notes, calls back ClassVistor of visitAnnotation method;
  • When accessing class members, calls back ClassVistor of visitField method;
  • When accessing class method calls back ClassVistor of visitMethod method;
  • .......

When the callback will visit different areas of the corresponding method, the method returns a byte code corresponding to the operation target. By modifying the object class you can modify the contents of the file corresponding to the respective structures. Finally, the bytecode content of this object overwrite the original file needs to be cut into .class bytecode.

2.2 ASM exemplary realization AOP

Code example below, the process method originally output only Base line "process."

Target : we expect the bytecode enhancement, output "start" before the implementation of the method, the output "end" after the method is executed.

public class Base {
    public void process() {
        System.out.println("process");
    }
}
复制代码

To achieve output code inserted before and after the process method requires the following steps:

  • (1) read Base.class file, by ClassReader read class file;
  • (2) configured System.out.println (String) bytecode, by ClassVisitor additional embedded bytecode appropriate place;
  • (3) In ClassVisitor process is completed, the ClassWriter new bytecode replace the old byte code;

The basic realization of the code reference to the following, SimpleGenerator class defines ClassReader and ClassWriter objects. Wherein ClassReader responsible for reading the bytecode, and then to ClassVisitor handling, after the completion of the replacement process is completed by the bytecode file ClassWriter.

public class SimpleGenerator {

    public static void main(String[] args) throws Exception {
        //读取类文件
        ClassReader classReader = new ClassReader("com.example.aop.asm.Base");
        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        //处理,通过classVisitor修改类
        ClassVisitor classVisitor = new SimpleClassVisitor(classWriter);
        classReader.accept(classVisitor, ClassReader.SKIP_DEBUG);
        byte[] data = classWriter.toByteArray();
        //保存新的字节码文件
        File file = new File("target/classes/com/example/aop/asm/Base.class");
        FileOutputStream outputStream = new FileOutputStream(file);
        outputStream.write(data);
        outputStream.close();
        System.out.println("generator new class file success.");
    }
}
复制代码

SimpleClassVisitor class inherits ClassVisitor class. ClassVisitor each method call in the following order, this order in the java doc ClassVisitor also described.

[ visitSource ] [ visitOuterClass ] ( visitAnnotation | visitTypeAnnotation | visitAttribute )* (visitInnerClass | visitField | visitMethod )* visitEnd.

Which visitMethod called when the access methods, we can achieve enhanced by extending the logic MethodVisitor method. Similarly, MethodVisitor also need to call its methods in the order.

( visitParameter )* [ visitAnnotationDefault ] ( visitAnnotation | visitAnnotableParameterCount | visitParameterAnnotation visitTypeAnnotation | visitAttribute )* [ visitCode ( visitFrame | visitXInsn | visitLabel | visitInsnAnnotation | visitTryCatchBlock | visitTryCatchAnnotation | visitLocalVariable | visitLocalVariableAnnotation | visitLineNumber )* visitMaxs ] visitEnd.

public class SimpleClassVisitor extends ClassVisitor {

    public SimpleClassVisitor(ClassVisitor classVisitor) {
        super(Opcodes.ASM5, classVisitor);
    }

    // visitMethod在访问类方法时回调,可以获知当前访问到方法信息
    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        MethodVisitor mv = super.visitMethod(access, name, desc, signature,
                exceptions);
        // 不需要增强Base类的构造方法
        if (mv != null && !name.equals("<init>")) {
            // 通过定制MethodVisitor,替换原来的MethodVisitor对象来实现方法改写
            mv = new SimpleMethodVisitor(mv);
        }
        return mv;
    }


    /**
     * 定制方法visitor
     */
    class SimpleMethodVisitor extends MethodVisitor {

        public SimpleMethodVisitor(MethodVisitor methodVisitor) {
            super(Opcodes.ASM5, methodVisitor);
        }

        // visitCode方法在ASM开始访问方法的Code区时回调
        @Override
        public void visitCode() {
            super.visitCode();
            // System.out.println("start")对应的字节码,在visitCode访问方法之后添加
            mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            mv.visitLdcInsn("start");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
        }

        // 每当ASM访问到无参数指令时,都会调用visitInsn方法
        @Override
        public void visitInsn(int opcode) {
            if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {
                // System.out.println("end")对应的字节码,在visitInsn方法返回前添加
                mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
                mv.visitLdcInsn("end");
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);

            }
            super.visitInsn(opcode);
        }

    }
}
复制代码

The method of operation SimpleGenerator main class to complete the Base class bytecode enhancement. class file shown by the enhanced results are decompiled FIG. See the corresponding code has changed, before and after increasing the output in the "start" and "end" of.

After the class bytecode enhancement Base

By ASM ByteCode Outline widget easily realize a mapping of source code byte.

3. References

Java bytecode enhancement Quest


I welcome the attention of the public to No.

Guess you like

Origin juejin.im/post/5dc95f52f265da4d082b9621
ASM
Recommended