1 byte code technology application scenario
- AOP Technology
- Lombok de duplication plug-in
- Dynamically modify class files, etc
2-byte technology advantage
Java bytecode enhancement refers to modifying and enhancing the function of Java bytecode after it is generated, which is equivalent to modifying the binary file of application program. Java bytecode enhancement is mainly to reduce redundant code and improve performance.
The main steps to enhance bytecode are as follows:
-
Modify bytecode
Get the original bytecode in memory, then modify its byte [] array through some tools (such as ASM, Javaasist) to get a new byte array. -
Make the modified bytecode effective
There are two ways:
1) Customize ClassLoader to load the modified bytecode;
2) Replace the original bytecode: when the JVM loads the user's Class, it intercepts and returns the modified bytecode; or at runtime, it replaces the original bytecode by using the Instrumentation.redefineClasses method
3 common bytecode operation class library
3.1 BCEL
Byte Code Engineering Library(BCEL), which is part of the Jakarta project of the Apache Software Foundation.
BCEL is a widely used framework of Java classworking, which allows you to go deep into the details of class library operation in jvm assembly language.
BCEL and javassist have different ways to deal with bytecode. BCEL operates at the actual jvm instruction level (BCEL has rich jvm instruction set support), while javassist emphasizes the work at the source code level.
3.2 ASM
It is a lightweight Java bytecode operation framework, which directly involves the operations and instructions at the bottom of the JVM
High performance, high quality
3.3 CGLB
Generation of class library based on ASM
3.4 Javassist
Is an open source analysis, editing and creation of Java bytecode class library. Performance is worse than ASM, similar to cglib, but easy to use. Many open source frameworks use it.
3.4.1 Javassist advantages
- It has less overhead and higher performance than reflection.
- javassist performance is higher than reflection and lower than ASM
The runtime operation bytecode enables us to achieve the following functions:
- Generate new classes dynamically
- Dynamically change the structure of a class (add / remove / modify new properties / methods)
The outermost API of javassist is quite similar to that in JAVA's reflection package.
It is mainly composed of several classes: CtClass, CtMethod, and CtField. It is used to perform the same operations as java.lang.Class, java.lang.reflect.Method, java.lang.reflect.Method.Field in the JDK reflection API.
Method operation
- Modify the method body of an existing method (insert code into the existing method body)
- New method
- Deletion method
3.4.2 limitations of javassist
The new syntax of JDK 5.0 does not support (including generics and enumerations), does not support annotation modification, but can be solved through the underlying javassist class. For details, refer to: javassist.bytecode.annotation
- Array initialization is not supported, such as String [] {"1", "2"}, unless only the array has a capacity of 1
- Inner and anonymous classes are not supported
- continue and break expressions are not supported.
Some do not support inheritance relationships. for example
- class A {}
- class B extends A {}
- class C extends B {}
4 javassist example
4.1 creating classes using Javassist
Import dependency:
<dependency> <groupId>javassist</groupId> <artifactId>javassist</artifactId> <version>3.12.1.GA</version> </dependency>
Create entity class:
package com.snow; public class User { private String name; private Integer age; }
Test:
package com.snow; import java.io.IOException; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtField; import javassist.CtMethod; import javassist.NotFoundException; /** * Using java bytecode technology to create bytecode * */ public class Test { public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException { ClassPool pool = ClassPool.getDefault(); // 1. Create user class CtClass userClass = pool.makeClass("com.snow.User"); // 2. Create name and age attributes CtField nameField = CtField.make(" private String name;", userClass); CtField ageField = CtField.make(" private Integer age;", userClass); // 3. Add attribute userClass.addField(nameField); userClass.addField(ageField); // 4. Creation method CtMethod nameMethod = CtMethod.make("public String getName() {return name;}", userClass); // 5. Adding method userClass.addMethod(nameMethod); // 6. Add constructor CtConstructor ctConstructor = new CtConstructor( new CtClass[] { pool.get("java.lang.String"), pool.get("java.lang.Integer") }, userClass); ctConstructor.setBody(" { this.name = name; this.age = age; }"); userClass.addConstructor(ctConstructor); // Generate class file userClass.writeFile("/Users/yangshuo/Desktop/learning"); } }
Execute the code and find that the class file is created:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.snow; public class User { private String name; private Integer age; public String getName() { return this.name; } public User(String var1, Integer var2) { this.name = this.name; this.age = this.age; } }
4.2 using Javassist to dynamically modify bytecode
package com.snow; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Test { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // 1. Use reflection technology to perform a method Class<?> forName = Class.forName("com.snow.Test"); Object newInstance = forName.newInstance(); Method method = forName.getDeclaredMethod("sum", int.class, int.class); method.invoke(newInstance, 1, 5); } static public void sum(int a, int b) { System.out.println("sum..."); } }
Console printing:
sum...
package com.snow; import java.lang.reflect.Method; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; /** * Dynamic modification of bytecode file * */ public class Test { public static void main(String[] args) { try { ClassPool pool = ClassPool.getDefault(); // Read com.itmayiedu.User CtClass userClass = pool.get("com.snow.User"); CtMethod method = new CtMethod(CtClass.voidType, "sum", new CtClass[] { CtClass.intType, CtClass.intType }, userClass); method.setBody("{System.out.println(\"sun:\" + ($1 + $2));}"); // Adding method userClass.addMethod(method); userClass.writeFile("/Users/yangshuo/Desktop/learning"); // Dynamic execution method Class clazz = userClass.toClass(); Object newInstance = clazz.newInstance(); Method sumMethod = clazz.getDeclaredMethod("sum", int.class, int.class); System.out.println("Opening things"); sumMethod.invoke(newInstance, 2, 5); // Using javassist to implement dynamic proxy. System.out.println("Submission of things"); } catch (Exception e) { e.printStackTrace(); } } }
Console printing:
Opening things sun:7 Submission of things