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 classesASM APIBased on theClassVisitorAbstract 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 dataClassVisitor;
  • ClassWriter: it inherits fromClassVisitor, 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 themCtClassIt’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 classCtClassRight;
  • CtMethod: the method in the corresponding class can passCtClassGets the specified method;
  • CtField: indicates an attribute in the corresponding classCtClassGets 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 proxiesProxyClasses are similar, except thatEnhancerBoth can represent ordinaryclass, can also be proxy interface;
  • MethodInterceptor: interceptor. When calling a target method,CGLibWill the callbackMethodInterceptorInterface method intercepts to implement your own proxy logic, similar toJDKIn theInvocationHandlerInterface;
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 specifyClassLoaderObject and a groupinterfaceTo 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.