preface
Bytecode is a Java file that has been compiled into a class file. Each bytecode file consists of 10 parts in a fixed order. Enhancement is actually the transformation of bytecode file to generate a new file, has achieved our purpose, such as dynamic proxy, AOP, etc.; Of course the enhancements need to be usable, so there are loading issues involved; Let’s take a look at some of the bytecode enhancement techniques available before we introduce them.
Common technology
Common bytecode enhancement techniques can be roughly divided into two categories: static enhancement and dynamic enhancement; The most common static enhancement is AspectJ, which compiles classes directly and has its own syntax; Dynamic enhancement includes: ASM, Javassist, Cglib, Java Proxy; The following are a brief introduction.
AspectJ
AspectJ, from the Eclipse Foundation, is static weaving, mainly compile-time weaving, during which AspectJ’s ACJ compiler (similar to JavAC) is used to compile aspect classes into class bytecode; Let’s look at how AspectJ is used;
-
Download and install AspectJ from www.eclipse.org/aspectj/. Download AspectJ 1.9.6. To install it, run the following command:
Java jar aspectj - 1.9.6. JarCopy the code
Specify the installation directory and configure classPath and path:
ASPECTJ_HOME = E: \ aspectj1.9 CLASSPATH =... %ASPECTJ_HOME%\lib\aspectjrt.jar PATH=... %ASPECTJ_HOME%\binCopy the code
-
Compile using the examples\ TJP directory that can be tested directly using the Demo provided by AspectJ: Java. Demo is our normal Java file, and GetInfo is an enhanced file that has some AspectJ syntax and needs to be compiled using ajC commands
E: aspectJ1.9 \doc\examples\ TJP > ajc-argfile files. LST E: aspectj1.9\doc\examples\ TJP > CD.. E:\ aspectJ1.9 \doc\examples> Java tjp.demo Intercepted message: foo in class: tjp.demo Arguments: 0. I: int = 1 1. o: java.lang.Object = tjp.Demo@6e3c1e69 Running original method: Demo.foo(1, tjp.Demo@6e3c1e69) result: null Intercepted message: bar in class: tjp.Demo Arguments: 0. j : java.lang.Integer = 3 Running original method: Demo.bar(3) result: Demo.bar(3) Demo.bar(3)Copy the code
You can see in the new AJC-compiled class file that both the go and bar methods have been enhanced with logging before and after method calls, as well as method parameters; You can see that AspectJ has enhanced the class file before it runs; Spring AOP borrows some concepts from AspectJ, but uses dynamic enhancement instead of AspectJ in its implementation.
ASM
ASM is a general-purpose Java bytecode manipulation and analysis framework that can be used to modify existing classes or generate classes dynamically directly in binary form. ASM provides common bytecode conversion and analysis algorithms from which you can build custom complex conversion and code analysis tools; A few core classes:
- ClassVisitor: Used to generate and convert compiled classes
ASM API
Based on theClassVisitor
Abstract classes, in which each method corresponds to a class file structure of the same name; - ClassReader: The main function of this class is to read bytecode files and notify the read data
ClassVisitor
; - ClassWriter: it inherits from
ClassVisitor
, mainly used to generate classes;
ASM partial low-level bytecode operation, all need to be familiar with bytecode command, but high performance; FastJson, Cglib, Lombok, and others rely on ASM; The basic operation steps are to first load the original Class file, then access all elements through the visitor mode, modify each element in the access process, and finally generate a bytecode binary file, load the new Class or reload according to the requirements.
For more information: Getting started with ASM
Javassist
Familiar with because the ASM bytecode would need to be ordered, and bytecode itself is obscure, all have the enhancement tools Javassist easier to understand, can directly use the Java coding way, don’t need to understand related bytecode instruction, more friendly to developers, performance is certainly not as good as the ASM, here have a look at a few common types:
- ClassPool is simply a pool of classes, all of them
CtClass
It’s all going to be taken from the pool; - CtClass: represents a class file, which can be obtained by the fully qualified name of a class
CtClass
Right; - CtMethod: the method in the corresponding class can pass
CtClass
Gets the specified method; - CtField: indicates an attribute in the corresponding class
CtClass
Gets the specified property;
Take a look at a simple logging enhancement example combining the above core classes:
public class JavassistTest {
public static void main(String[] args) throws Exception {
// The path to the enhanced class information
CtClass.debugDump = "./dump";
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("com.zh.asm.TestService");
CtMethod m = cc.getDeclaredMethod("query");
m.insertBefore("{ System.out.println(\"start\"); }");
m.insertAfter("{ System.out.println(\"end\"); }"); TestService h = (TestService) c.newInstance(); h.query(); }}Copy the code
Dubbo uses Javassist to do dynamic compilation processing, and JBoss uses Javassist to do AOP processing.
More: www.javassist.org/tutorial/tu…
Cglib
Cglib is a powerful, high-performance code generation package that relies on ASM at the bottom. Provides a nice complement to the JDK’s dynamic proxy. Java proxies do not support the absence of interfaces, and Cglib is much more powerful. The core classes are as follows:
- Enhancer:
Cglib
, and dynamic proxiesProxy
Classes are similar, except thatEnhancer
Both can represent ordinaryclass
, can also be proxy interface; - MethodInterceptor: interceptor. When calling a target method,
CGLib
Will the callbackMethodInterceptor
Interface method intercepts to implement your own proxy logic, similar toJDK
In theInvocationHandler
Interface;
public class CgLibProxy {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:/asm/cglib");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TestService.class);
enhancer.setCallback(newMyMethodInterceptor()); TestService testService = (TestService)enhancer.create(); testService.query(); }}public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Enhanced processing");
Object object = proxy.invokeSuper(obj, args);
returnobject; }}Copy the code
Cglib dynamically generates a proxy class, which is actually a proxy class when executed. In the proxy class, the original class is called to achieve functionality enhancement.
Java Proxy
Create proxy classes at run time using reflection; The interface and the proxied class remain unchanged, and a handler class is constructed to implement the InvocationHandler interface. Core classes are as follows:
- Proxy: to specify
ClassLoader
Object and a groupinterface
To create dynamic proxy classes; - InvocationHandler: Create your own call handler, which is enhanced processing;
public class MyHandler implements InvocationHandler{
private Object object;
public MyHandler(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before invoke...");
method.invoke(object, args);
System.out.println("After invoke...");
return null; }}Copy the code
The constructor of dynamic proxy class is obtained through reflection mechanism, and the instance of dynamic proxy class is created through the constructor. Cglib relies on ASM for better reflection performance than Cglib. FastJson uses ASM instead of reflection, and Spring AOP implementations use Cglib.
Loading problem
The above makes a simple introduction to various bytecode enhancement technologies. No matter which kind of enhancement technology, the class information after the enhancement needs to be loaded. According to the use of different enhancement technologies and different functions of enhancement, the method of class loading is also different.
Static compilation
In this case, the AspectJ technique described above, the class is enhanced before it is run, so it is loaded in the same way as normal classes and may not be aware of the class file enhancements at run time. This is the easiest way to load classes;
A dynamic proxy
This method uses dynamic enhancement techniques to create a proxy class, which has its own unique name. The proxy class implements the interface class or inherits from the original class. In fact, it generates a new class. FastJson also uses ASM’s class generation capabilities; So in this case just prepare a class load; ASM does not provide class loading, as several other dynamic enhancement technologies do;
Hot update
This is the most complex case, how to reload the class after the bytecode enhancement if the class that needs to be enhanced has already been loaded into memory; Instrument is a library provided by the JVM that modifies loaded classes and supports staking services written specifically in the Java language. Prior to JDK 1.6, Instrument only worked when the JVM started loading classes. After JDK 1.6, Instrument supported changes to class definitions at runtime. To do this, you need to provide a ClassFileTransformer implementation class that implements a transform method that returns a byte array that can be generated using the bytecode enhancement tools described above.
With Instrumentation, developers can build an application-independent Agent to monitor and assist programs running on the JVM, and even replace and modify the definition of certain classes. With this capability, developers can implement more flexible runtime virtual machine monitoring and Java class manipulation, which in effect provides a virtual-machine-supported implementation of AOP that allows developers to implement some of THE functionality of AOP without making any upgrades or changes to the JDK.
The instrument mechanism described above relies on JPDA: Java Platform Debugger Architecture (JPDA), which is a set of interfaces provided by Java virtual machines (VMS) to debug and monitor VMS. JPDA is composed of three specifications: JVMTI (JVM Tool Interface), JDWP (Java Debug Wire Protocol) and JDI (Java Debug Interface).
More: docs.oracle.com/javase/8/do…
conclusion
This article briefly introduces some commonly used bytecode enhancement technologies. It is the support of these underlying technologies that help us greatly improve the efficiency of development in the process of development, such as Lombok and AOP. Improving performance such as FastJson and ReflectASM; Cooperate with Java Agent for hot updates and so on.
Thank you for attention
You can pay attention to the wechat public account “Roll back the code”, read the first time, the article continues to update; Focus on Java source code, architecture, algorithms, and interviews.