Brief Analysis of JDK Dynamic Agent and CGLIB Dynamic Agent

Posted by tommyrulez on Tue, 21 Sep 2021 01:34:27 +0200

Analysis of Agent Model

1. What is proxy mode


What is the agent mode? First of all, a simple example is Zhang San (user) wants a ticket for a concert but he has no channel to buy it. Then his friend Li Si (agent) said he can buy tickets for a concert (agent method).In some cases, one object is not suitable or cannot directly refer to another object, while the proxy object can be in the client and destination.There are two main purposes to use the proxy mode: first, to protect the target object, but to enhance it.

Proxy Mode Class Diagram

Subject is the top-level interface and RealSubject is the real object (the Proxy object), ProxyIs a Proxy object, the Proxy object holds a reference to the Proxy object, the client calls the Proxy object method, and at the same time calls the method of the Proxy object, but adds some processing before and after the Proxy object. In the code, when we think of the Proxy, we understand that it is code enhancement, that is, adding some logic before and after the original logic, but the caller is not aware of the Proxy mode.It is structured with static and dynamic agents.

2. Static Proxy

What is a static proxy? In fact, this dynamic and static difference is for the program. The so-called static means that the byte code file of the proxy class already exists before the program runs. The relationship between the proxy class and the delegate class is determined before the program runs. Here is a simple static proxy example written in the code.

public class Order {
    private Object orderInfo;
    private Long createTime;
    private String id;

    public Object getOrderInfo() {
        return orderInfo;
    }

    public void setOrderInfo(Object orderInfo) {
        this.orderInfo = orderInfo;
    }

    public Long getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Long createTime) {
        this.createTime = createTime;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}
public class OrderService implements IOrderService {
    private OrderDao orderDao;

    public OrderService() {
		//If Spring is used it should be injected automatically
		//For ease of use, we initialized orderDao directly in the construction method
        orderDao = new OrderDao();
    }

    @Override
    public int createOrder(Order order) {
        System.out.println("OrderService call orderDao Create Order");
        return orderDao.insert(order);
    }
}
public interface IOrderService {
    int createOrder(Order order);
}

There is an IOrderService interface, there is a method to create orders, OrderService implements the IOrderService interface, and this method to create orders. Now in a distributed environment, the database needs to be subdivided into tables, after subdivided tables are finished using JAVA operation, you need to configure multiple data sources, we switch dynamically by setting data source routingData source. According to the open and close principle, the written logic is unchanged and modified by the way of proxy object. Our main function is to automatically repository by year according to the creation time of the order. DynamicDataSourceEntry (proxy class) is written by ThreadLocal to dynamically switch the classes of the data source.

/**
 * @author: Winston
 * @createTime: 2021/6/15
 * Switching data sources dynamically
 */
public class DynamicDataSourceEntry {
    // Default data source
    public final static String DEFAULT_SOURCE = null;
    private final static ThreadLocal<String> local = new ThreadLocal<String>();

    private DynamicDataSourceEntry(){}

    /**
     * Empty Data Source
     */
    public static void clear(){
        local.remove();
    }

    /**
     * Get the name of the data source currently in use
     * @return
     */
    public static String get(){
       return local.get();
    }

    /**
     *Restore the data source of the current facet
     */
    public static void restore(){
        local.set(DEFAULT_SOURCE);
    }

    /**
     * Set up a data source for well-known words
     * @param source
     */
    public static void set(String source){
        local.set(source);
    }

    /**
     * Set data source dynamically based on year
     * @param year
     */
    public static void set(int year) {
        local.set("DB_" + year);
    }
}

The next step is to write a proxy class to implement the IOrderService interface, through which the proxy class calls the proxy class and completes the method calls to the proxy class. On the basis of the existing functions, the business code is not modified to complete the work of data source switching.

/**
 * @author: Winston
 * @createTime: 2021/6/15
 */
public class OrderServiceStaticProxy implements IOrderService {
    private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
    private IOrderService orderService;

    public OrderServiceStaticProxy(IOrderService orderService){
        this.orderService = orderService;
    }

    @Override
    public int createOrder(Order order) {
        before();
        Long time = order.getCreateTime();
        Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
        System.out.println("Static proxy classes are automatically assigned to DB_" + dbRouter + "]The data source processes the data.");
        DynamicDataSourceEntry.set(dbRouter);
        orderService.createOrder(order);
        after();
        return 0;
    }

    private void before(){
        System.out.println("Proxy before method.");
    }
    private void after(){
        System.out.println("Proxy after method.");
    }
}

Test Code

/**
 * @author: Winston
 * @createTime: 2021/6/15
 */
public class StaticProxyTest {
    public static void main(String[] args) throws ParseException {
        Order order = new Order();
// Date today = new Date();
// order.setCreateTime(today.getTime());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        Date date = sdf.parse("2017/02/01");
        order.setCreateTime(date.getTime());
        IOrderService orderService = new OrderServiceStaticProxy(new OrderService());
        orderService.createOrder(order);

    }
}

Run Results

Proxy before method.
Static proxy classes are automatically assigned to DB_2017]The data source processes the data.
OrderService call orderDao Create Order
OrderDao Establish Order Success!
Proxy after method.

Static Proxy Summary

Advantages: There is no need to modify the code of the business class, the business class only needs to focus on the business itself, which is a common advantage of the agents

Disadvantages:

  1. An interface of a proxy object serves only one type of object. If there are many ways to proxy, it is necessary to proxy each method. For example, to add print logs to all methods, all methods need to be proxied, so static proxies are not competent when the program is a little larger.
  2. If a new method is added to the interface, all proxy classes will implement it in addition to all implementation classes, which makes code maintenance more difficult.

2. Dynamic Proxy

The source code of the dynamic proxy class is generated dynamically by the JVM during the running of the program according to the reflection mechanism, so there is no byte code file for the proxy class. The relationship between the proxy class and the delegate class is determined at the run-time of the program. If the father finds objects for his son, for example, the father is like a static proxy, looking for objects only for his son, regardless of whether someone else wants to find objects or not.Dynamic proxy can adapt to complex business scenarios. Matchmaker is like a dynamic proxy and can find objects for any single person, not just son. Similarly, we write an example of dynamic proxy with code.

JDK Dynamic Proxy

/**
 * @author: Winston
 * @createTime: 2021/6/16
 */
public interface Person {

    /**
     * Affinity Method
     */
    public void findLove();
}

/**
 * @author: Winston
 * @createTime: 2021/6/16
 */
public class Girl implements  Person{

    /**
     * Find Object Requirements
     */
    @Override
    public void findLove() {
        System.out.println("Tall, rich and handsome");
        System.out.println("Height 180 cm");
        System.out.println("Weight 73 kg");
        System.out.println("Two Suites in the house");
    }
}
/**
 * @author: Winston
 * @createTime: 2021/6/16
 */
public class JDKMeipo implements InvocationHandler {

    /**
     * Proxy Object
     */
    private Object target;

    public Object getInstance(Object target){
        this.target = target;
        // Get class information
        Class clazz = target.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target, args);
        after();
        return result;
    }

    /**
     * Pre-method
     */
    public void before(){
        System.out.println("I'm a matchmaker and I'm ready to start looking for the right match for you");
    }

    /**
     * Postmethod
     */
    public void after(){
        System.out.println("Get ready for hey hey if the conditions are right");
    }
}

test method

/**
 * @author: Winston
 * @createTime: 2021/6/16
 */
public class JdkDynamicProxyTest {
    public static void main(String[] args) {

        Person person = (Person) new JDKMeipo().getInstance(new Girl());
        person.findLove();
    }
}

Output Results

I'm a matchmaker and I'm ready to start looking for the right match for you
 Tall, rich and handsome
 Height 180 cm
 Weight 73 kg
 Two Suites in the house
 Get ready for hey hey if the conditions are right

From the result, we can see that the pre-method, findLove method and post-method are called, but there is no findLove method in JDKMeipo and no pre-post method in person. So which class is called?

With debug mode, we can see that it's a brand new class, $Proxy0, which we generate byte code files for and decompile with jad to see what we've written.

/**
 * @author: Winston
 * @createTime: 2021/6/16
 */
public class JdkDynamicProxyTest {
    public static void main(String[] args) {

        Person person = (Person) new JDKMeipo().getInstance(new Girl());
        person.findLove();

        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
        try {
            FileOutputStream fileOutputStream = new FileOutputStream("H://$Proxy0.class");
            fileOutputStream.write(bytes);
            fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 

import com.gupaoedu.vip.pattern.proxy.service.Person;
import java.lang.reflect.*;

public final class $Proxy0 extends Proxy
    implements Person
{

    public $Proxy0(InvocationHandler invocationhandler)
    {
        super(invocationhandler);
    }

    public final boolean equals(Object obj)
    {
        try
        {
            return ((Boolean)super.h.invoke(this, m1, new Object[] {
                obj
            })).booleanValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void findLove()
    {
        try
        {
            super.h.invoke(this, m3, null);
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString()
    {
        try
        {
            return (String)super.h.invoke(this, m2, null);
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode()
    {
        try
        {
            return ((Integer)super.h.invoke(this, m0, null)).intValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    static 
    {
        try
        {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
                Class.forName("java.lang.Object")
            });
            m3 = Class.forName("com.gupaoedu.vip.pattern.proxy.service.Person").getMethod("findLove", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        }
        catch(NoSuchMethodException nosuchmethodexception)
        {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch(ClassNotFoundException classnotfoundexception)
        {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}

By looking at p r o x y 0 back Edit translate after Of generation code I They can with see reach , Yes m 1 , m 3 , m 2 , m 0 four individual square method , this four individual square method yes through too back shoot sweep Tracing Out come Of , The decompiled proxy0 code shows that there are four methods, m1,m3,m2,m0, which are scanned by reflection. The decompiled proxy0 code shows that there are four methods, m1,m3,m2,m0, which are scanned by reflection. Proxy0 inherits the Proxy class and implements the Person interface at the same time. m3 is the findLove method of the Person class, and $Proxy0 implements the findLove method at the same time. Let's see the code.

  public final void findLove()
    {
        try
        {
            super.h.invoke(this, m3, null);
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(thr	owable);
        }
    }

The findLove method calls super.h.invoke(this, m3, null);,This h is the InvocationHandler interface in Proxy. Since JDKMeipo implements the InvocationHandler interface, super.h.invoke(this, m3, null);The invoke method of JDKMeipo is actually called.

JDK Dynamic Proxy Summary

In fact, the JDK dynamic proxy uses byte reorganization to regenerate the object instead of the original object to achieve the purpose of dynamic proxy. The steps of JDK Proxy to generate the object are as follows, taking the matchmaker as an example.

  1. Get the reference to the proxy object (Girl), get all its interfaces (findLove method in this example), and reflect the fetch. (This is why the JDK dynamic proxy requires the proxy class and the proxy class to have the same interface beforehand.)
  2. The JDK Proxy class regenerates a new class that requires all interfaces (findLove) that implement all the implementations of the proxy class (Girl)
  3. Generate java code dynamically to invoke new business logic methods from a specific logic code (reflected in the code)
  4. Compile the newly generated java code.class
  5. Reload to run in JVM

This process is called byte code reassembly. There is a specification in the JDK that class files that start with $are usually automatically generated under ClassPath.

Dynamic Switching Data Source Case Modification

In the previous dynamic data source switching scenario, we used a static proxy, and we will now transform it into a dynamic proxy.

/**
 * @Author winston
 * @Date 2021/6/20
 * Change order data source switching case from static proxy to JDK dynamic proxy
 */
public class JDKOrderProxy implements InvocationHandler {
    private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");

    /**
     * Proxy Object
     */
    private Object proxyObj;

    public Object getInstance(Object proxyObj) {
        // Assign Proxy Object
        this.proxyObj = proxyObj;
        // Get class information
        Class<?> clazz = proxyObj.getClass();
        // Return Object
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before(args[0]);
        Object result = method.invoke(proxyObj, args);
        after();
        return result;
    }

    /**
     * Switching Data Sources
     *
     * @param target Should be the order object
     */
    private void before(Object target) {
        try {
            System.out.println("Switching Data Sources");
            // Conventions are better than configurations, so we agreed that there is a getCreateTime method, which is reflected
            Method method = target.getClass().getMethod("getCreateTime");
            Long time = (Long) method.invoke(target);
            Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
            System.out.println("Static proxy classes are automatically assigned to DB_" + dbRouter + "]The data source processes the data.");
            DynamicDataSourceEntry.set(dbRouter);
//            orderService.createOrder(order);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Reset Data Source
     *
     */
    private void after() {
        System.out.println("Reset Data Source");
        DynamicDataSourceEntry.restore();
    }
}

Test Code

/**
 * @author: Winston
 * @createTime: 2021/6/16
 */
public class JdkDynamicProxyOrderTest {
    public static void main(String[] args) throws ParseException {
        Order order = new Order();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        Date date = sdf.parse("2018/02/01");
        order.setCreateTime(date.getTime());
        IOrderService orderService = (IOrderService) new JDKOrderProxy().getInstance(new OrderService());
        orderService.createOrder(order);
    }
}

CGLIB Dynamic Proxy

Reference Blog: CGLIB Dynamic Proxy Details

The CGLIB dynamic proxy is structured as follows. We can see that the proxy class inherits the target class. Each time a proxy class is called, its method is intercepted by the method interceptor, which is the logic for calling the method of the target class.

First we can feel the CGLIB dynamic proxy through the code, and we also transform it with the matchmaker case.

/**
 * @author: Winston
 * @createTime: 2021/6/22
 */
public class CglibMeipoProxy implements MethodInterceptor {

    /**
     * Gets the proxy object, where the parameter is of type Class
     * Why is the class type, because the parameter received here is the parent class, which we need to inherit
     * Override method generates new class
     * @param clazz
     * @return
     */
    public Object getInstance(Class clazz) {
        //Creating an Enhancer object, similar to the Proxy class of the JDK dynamic proxy, the next step is to set several parameters
        Enhancer enhancer = new Enhancer();
        //Set Target Class
        enhancer.setSuperclass(clazz);
        // Set the intercept, which is the intercept method for this class (CglibMeipoProxy)
        enhancer.setCallback(this);
        // Generate proxy class and return an instance
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        // Call the proxy method, note here that method calls do not use reflection
        Object result = methodProxy.invokeSuper(o, objects);
        after();
        return result;
    }


    /**
     * Pre-method
     */
    public void before(){
        System.out.println("I'm a matchmaker and I'm ready to start looking for the right match for you");
    }

    /**
     * Postmethod
     */
    public void after(){
        System.out.println("Get ready for hey hey if the conditions are right");
    }
}

Test Class

/**
 * @author: Winston
 * @createTime: 2021/6/22
 */
public class CglibMeipoProxyTest {
    public static void main(String[] args) {
        //Using cglib's proxy class, the in-memory class file can be written to the local disk
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"H://cglib_proxy_class");
        Boy boy = (Boy) new CglibMeipoProxy().getInstance(Boy.class);
        boy.findLove();
    }

}

After running the test class, we look in the specified directory and see that there are three byte code files generated, one is the FastClass of the proxy class (Boy$$EnhancerByCGLIB$$71710d07$$FastClassByCGLIB$$e2a848ac.class), the other is the proxy class (Boy$$EnhancerByCGLIB$$71710d07.class), and the other is the FastClass of the target class (Boy$$FastByCGLIB$$364eb7e9.class)To briefly describe what FastClass is, each method is numbered to find a method that avoids the inefficiency associated with frequent use of reflection.

Let's take a brief look at the contents of the file by decompiling the class file of the proxy class into a java file using the jad tool. We can see that for the eat method, there will be findLove and CGLIB$findLove$0 in the proxy class; the former is the method we call when using the proxy class, and the latter is called in the method interceptor, in other wordsLet's say that when our code calls the findLove method of the proxy object, it then calls the intercept method in the method interceptor, where it calls the CGLIB$findLove$0 method through proxy.invokeSuper.

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   <generated>

package com.gupaoedu.vip.pattern.proxy.service;

import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.*;

// Referenced classes of package com.gupaoedu.vip.pattern.proxy.service:
//            Boy

// This proxy class inherits our target class Boy and implements the Factory interface, which is some way to set callback functions and return instantiated objects
public class Boy$$EnhancerByCGLIB$$71710d07 extends Boy
    implements Factory
{
    static void CGLIB$STATICHOOK1()
    {
        //Note the two Method arrays below to save the Method object obtained by reflection, so as to avoid using reflection to get the Method object each time
        Method amethod[];
        Method amethod1[];
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        //Get the byte code file of the target class
        Class class1 = Class.forName("com.gupaoedu.vip.pattern.proxy.service.Boy$$EnhancerByCGLIB$$71710d07");
        //Byte code file for proxy class
        Class class2;
        //ReflectUtils is a tool class that wraps various reflection operations to get Method objects for each method and save them to the Method array above
        amethod = ReflectUtils.findMethods(new String[] {
            "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"
        }, (class2 = Class.forName("java.lang.Object")).getDeclaredMethods());
        Method[] _tmp = amethod;
        //Indexing each method of the target class imagines recording the address of all the methods in the target class, which can be found directly from the address when the target class method is called
        //This is what CGLIB$xxxxx$$Proxy does here.
        CGLIB$equals$1$Method = amethod[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(class2, class1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
        CGLIB$toString$2$Method = amethod[1];
        CGLIB$toString$2$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
        CGLIB$hashCode$3$Method = amethod[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(class2, class1, "()I", "hashCode", "CGLIB$hashCode$3");
        CGLIB$clone$4$Method = amethod[3];
        CGLIB$clone$4$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
        amethod1 = ReflectUtils.findMethods(new String[] {
            "findLove", "()V"
        }, (class2 = Class.forName("com.gupaoedu.vip.pattern.proxy.service.Boy")).getDeclaredMethods());
        Method[] _tmp1 = amethod1;
        CGLIB$findLove$0$Method = amethod1[0];
        CGLIB$findLove$0$Proxy = MethodProxy.create(class2, class1, "()V", "findLove", "CGLIB$findLove$0");
    }
    //This method is to call the findLove method of the target class
    final void CGLIB$findLove$0()
    {
        super.findLove();
    }

    //This method is what we're going to call, and in the previous example, calling the findLove method of the proxy object brings us to this method
    public final void findLove()
    {
        CGLIB$CALLBACK_0;
        if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 37;
           goto _L3 _L4
_L3:
        break MISSING_BLOCK_LABEL_21;
_L4:
        break MISSING_BLOCK_LABEL_37;
        this;
        CGLIB$findLove$0$Method;
        CGLIB$emptyArgs;
        CGLIB$findLove$0$Proxy;
        //This is the intecept() method that calls the method interceptor
        intercept();
        return;
        super.findLove();
        return;
    }

    final boolean CGLIB$equals$1(Object obj)
    {
        return super.equals(obj);
    }

    public final boolean equals(Object obj)
    {
        CGLIB$CALLBACK_0;
        if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 57;
           goto _L3 _L4
_L3:
        this;
        CGLIB$equals$1$Method;
        new Object[] {
            obj
        };
        CGLIB$equals$1$Proxy;
        intercept();
        JVM INSTR dup ;
        JVM INSTR ifnonnull 50;
           goto _L5 _L6
_L5:
        JVM INSTR pop ;
        false;
          goto _L7
_L6:
        (Boolean);
        booleanValue();
_L7:
        return;
_L4:
        return super.equals(obj);
    }

    final String CGLIB$toString$2()
    {
        return super.toString();
    }

    public final String toString()
    {
        CGLIB$CALLBACK_0;
        if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 40;
           goto _L3 _L4
_L3:
        this;
        CGLIB$toString$2$Method;
        CGLIB$emptyArgs;
        CGLIB$toString$2$Proxy;
        intercept();
        (String);
        return;
_L4:
        return super.toString();
    }

    final int CGLIB$hashCode$3()
    {
        return super.hashCode();
    }

    public final int hashCode()
    {
        CGLIB$CALLBACK_0;
        if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 52;
           goto _L3 _L4
_L3:
        this;
        CGLIB$hashCode$3$Method;
        CGLIB$emptyArgs;
        CGLIB$hashCode$3$Proxy;
        intercept();
        JVM INSTR dup ;
        JVM INSTR ifnonnull 45;
           goto _L5 _L6
_L5:
        JVM INSTR pop ;
        0;
          goto _L7
_L6:
        (Number);
        intValue();
_L7:
        return;
_L4:
        return super.hashCode();
    }

    final Object CGLIB$clone$4()
        throws CloneNotSupportedException
    {
        return super.clone();
    }

    protected final Object clone()
        throws CloneNotSupportedException
    {
        CGLIB$CALLBACK_0;
        if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 37;
           goto _L3 _L4
_L3:
        this;
        CGLIB$clone$4$Method;
        CGLIB$emptyArgs;
        CGLIB$clone$4$Proxy;
        intercept();
        return;
_L4:
        return super.clone();
    }

    public static MethodProxy CGLIB$findMethodProxy(Signature signature)
    {
        String s = signature.toString();
        s;
        s.hashCode();
        JVM INSTR lookupswitch 5: default 120
    //                   -508378822: 60
    //                   1192015562: 72
    //                   1826985398: 84
    //                   1913648695: 96
    //                   1984935277: 108;
           goto _L1 _L2 _L3 _L4 _L5 _L6
_L2:
        "clone()Ljava/lang/Object;";
        equals();
        JVM INSTR ifeq 121;
           goto _L7 _L8
_L8:
        break MISSING_BLOCK_LABEL_121;
_L7:
        return CGLIB$clone$4$Proxy;
_L3:
        "findLove()V";
        equals();
        JVM INSTR ifeq 121;
           goto _L9 _L10
_L10:
        break MISSING_BLOCK_LABEL_121;
_L9:
        return CGLIB$findLove$0$Proxy;
_L4:
        "equals(Ljava/lang/Object;)Z";
        equals();
        JVM INSTR ifeq 121;
           goto _L11 _L12
_L12:
        break MISSING_BLOCK_LABEL_121;
_L11:
        return CGLIB$equals$1$Proxy;
_L5:
        "toString()Ljava/lang/String;";
        equals();
        JVM INSTR ifeq 121;
           goto _L13 _L14
_L14:
        break MISSING_BLOCK_LABEL_121;
_L13:
        return CGLIB$toString$2$Proxy;
_L6:
        "hashCode()I";
        equals();
        JVM INSTR ifeq 121;
           goto _L15 _L16
_L16:
        break MISSING_BLOCK_LABEL_121;
_L15:
        return CGLIB$hashCode$3$Proxy;
_L1:
        JVM INSTR pop ;
        return null;
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(Callback acallback[])
    {
        CGLIB$THREAD_CALLBACKS.set(acallback);
    }

    public static void CGLIB$SET_STATIC_CALLBACKS(Callback acallback[])
    {
        CGLIB$STATIC_CALLBACKS = acallback;
    }
    //This method is called in a static block of code
    private static final void CGLIB$BIND_CALLBACKS(Object obj)
    {
        Boy$$EnhancerByCGLIB$$71710d07 boy$$enhancerbycglib$$71710d07 = (Boy$$EnhancerByCGLIB$$71710d07)obj;
        if(boy$$enhancerbycglib$$71710d07.CGLIB$BOUND) goto _L2; else goto _L1
_L1:
        Object obj1;
        boy$$enhancerbycglib$$71710d07.CGLIB$BOUND = true;
        obj1 = CGLIB$THREAD_CALLBACKS.get();
        obj1;
        if(obj1 != null) goto _L4; else goto _L3
_L3:
        JVM INSTR pop ;
        CGLIB$STATIC_CALLBACKS;
        if(CGLIB$STATIC_CALLBACKS != null) goto _L4; else goto _L5
_L5:
        JVM INSTR pop ;
          goto _L2
_L4:
        (Callback[]);
        boy$$enhancerbycglib$$71710d07;
        JVM INSTR swap ;
        0;
        JVM INSTR aaload ;
        (MethodInterceptor);
        CGLIB$CALLBACK_0;
_L2:
    }

    public Object newInstance(Callback acallback[])
    {
        CGLIB$SET_THREAD_CALLBACKS(acallback);
        CGLIB$SET_THREAD_CALLBACKS(null);
        return new Boy$$EnhancerByCGLIB$$71710d07();
    }

    public Object newInstance(Callback callback)
    {
        CGLIB$SET_THREAD_CALLBACKS(new Callback[] {
            callback
        });
        CGLIB$SET_THREAD_CALLBACKS(null);
        return new Boy$$EnhancerByCGLIB$$71710d07();
    }

    public Object newInstance(Class aclass[], Object aobj[], Callback acallback[])
    {
        CGLIB$SET_THREAD_CALLBACKS(acallback);
        JVM INSTR new #2   <Class Boy$$EnhancerByCGLIB$$71710d07>;
        JVM INSTR dup ;
        aclass;
        aclass.length;
        JVM INSTR tableswitch 0 0: default 35
    //                   0 28;
           goto _L1 _L2
_L2:
        JVM INSTR pop ;
        Boy$$EnhancerByCGLIB$$71710d07();
          goto _L3
_L1:
        JVM INSTR pop ;
        throw new IllegalArgumentException("Constructor not found");
_L3:
        CGLIB$SET_THREAD_CALLBACKS(null);
        return;
    }

    public Callback getCallback(int i)
    {
        CGLIB$BIND_CALLBACKS(this);
        this;
        i;
        JVM INSTR tableswitch 0 0: default 30
    //                   0 24;
           goto _L1 _L2
_L2:
        CGLIB$CALLBACK_0;
          goto _L3
_L1:
        JVM INSTR pop ;
        null;
_L3:
        return;
    }

    public void setCallback(int i, Callback callback)
    {
        switch(i)
        {
        case 0: // '\0'
            CGLIB$CALLBACK_0 = (MethodInterceptor)callback;
            break;
        }
    }

    public Callback[] getCallbacks()
    {
        CGLIB$BIND_CALLBACKS(this);
        this;
        return (new Callback[] {
            CGLIB$CALLBACK_0
        });
    }

    public void setCallbacks(Callback acallback[])
    {
        this;
        acallback;
        JVM INSTR dup2 ;
        0;
        JVM INSTR aaload ;
        (MethodInterceptor);
        CGLIB$CALLBACK_0;
    }
    //There are many attributes here, so if you look closely, there are two corresponding methods, one is Method type and the other is MethodProxy type.
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback CGLIB$STATIC_CALLBACKS[];
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static Object CGLIB$CALLBACK_FILTER;
    private static final Method CGLIB$findLove$0$Method;
    private static final MethodProxy CGLIB$findLove$0$Proxy;
    private static final Object CGLIB$emptyArgs[];
    private static final Method CGLIB$equals$1$Method;
    private static final MethodProxy CGLIB$equals$1$Proxy;
    private static final Method CGLIB$toString$2$Method;
    private static final MethodProxy CGLIB$toString$2$Proxy;
    private static final Method CGLIB$hashCode$3$Method;
    private static final MethodProxy CGLIB$hashCode$3$Proxy;
    private static final Method CGLIB$clone$4$Method;
    private static final MethodProxy CGLIB$clone$4$Proxy;
    //A static block of code that calls the following static method, which roughly obtains the MethodProxy object for each method in the target method
    static 
    {
        CGLIB$STATICHOOK1();
    }
    //Parameterless constructor
    public Boy$$EnhancerByCGLIB$$71710d07()
    {
        CGLIB$BIND_CALLBACKS(this);
    }
}

Analysis of FastClass Mechanism

Why use this mechanism? It's great to use reflection directly, but we know that reflection, while useful, is slightly slower than direct new objects, so we have this mechanism, refer to this blog https://www.cnblogs.com/cruze/p/3865180.html There is a small example that is very clear.

public class test10 {  //Here, tt can be seen as the target object, fc can be seen as the proxy object; first, the index of the target method is obtained according to the getIndex method of the proxy object, and //then invoke method of the proxy object to call the method of the target class directly, avoiding reflection
    public static void main(String[] args){
        Test tt = new Test();
        Test2 fc = new Test2();
        int index = fc.getIndex("f()V");
        fc.invoke(index, tt, null);
    }
}

class Test{
    public void f(){
        System.out.println("f method");
    }
    
    public void g(){
        System.out.println("g method");
    }
}
class Test2{
    public Object invoke(int index, Object o, Object[] ol){
        Test t = (Test) o;
        switch(index){
        case 1:
            t.f();
            return null;
        case 2:
            t.g();
            return null;
        }
        return null;
    }
    //This method indexes the methods in the Test class
    public int getIndex(String signature){
        switch(signature.hashCode()){
        case 3078479:
            return 1;
        case 3108270:
            return 2;
        }
        return -1;
    }
}

CGLIB Dynamic Proxy Summary

We looked at the use of the CGLib dynamic proxy, the actual generated proxy class, and the FastClass mechanism above. Below we'll look at the main calling steps by calling the findLove() method in the previous example.

The first step is to instantiate the Enhance object through a series of operations, set the required parameters, and then enhancer.create() successfully created the proxy object, let alone say that...

Step 2: Calling the findLove() method of the proxy object will enter the intercept() method of the method interceptor, in which proxy.invokeSuper(obj, args) is called;Method

Step 3: In invokeSuper, invoke the method of the target class through the FastClass mechanism

Comparison of CGLIB Dynamic Proxy and JDK Dynamic Proxy

  1. JDK dynamic proxy implements the interface of the proxy object, CGLIB dynamic proxy inherits the proxy object
  2. JDK and CGLIB both generate byte codes at run time, JDK directly generates Class byte codes, CGLIB uses ASM framework to write Class byte codes, CGLIB proxy implementation is more complex, so proxy class generation is less efficient than JDK.
  3. JDK invokes proxy methods through reflection mechanism, while CGLIB invokes methods directly through FastClass mechanism, so CGLIB executes more efficiently.

Principles for agent selection in Spring

  1. Spring uses JDK's dynamic proxy when Bean has an implementation interface
  2. Spring uses Cglib dynamic proxy when Bean does not implement an interface
  3. Spring can enforce the use of Cglib dynamic proxies by simply adding the following code to Spring's configuration file
<aop:aspectj-autoproxy proxy-target-class="true"/>

Topics: Java Spring Design Pattern