preface

Cglib dynamic proxies are more difficult to understand than cglib dynamic proxies. The JDK dynamic proxy is more difficult to understand than cglib dynamic proxies. Cglib’s dynamic proxies are in their element once you understand them.

The basic principle of

Cglib implements dynamic proxying by generating a subclass of the proxied class. That is, the generated dynamic proxy class inherits the proxied class. This will become clear later when the dynamic proxy class is decompiled.

What can you learn

  1. Why can Cglib implement dynamic proxies based on classes, and what are the essential differences between cglib and JDK dynamic proxies?
  2. How does cglib invoke methods of proxied classes?
  3. How does Cglib output proxy classes locally?
  4. Additional usemethodProxy.invoke()Why stack overflow?
  5. How does cglib create a proxy class (not including the ASM section)?
  6. How does Cglib perform the proxy method? What is the difference between JDK dynamic proxy executing proxy methods?

use

As in the previous article, we will continue to use the example from the previous article for ease of understanding.

Step one

Create a class that needs to be proxied. Because Cglib can be proxied based on classes (and interfaces), we don’t need to create an interface, just create a class.

public class Hello {
    public void sayHello(a){
        System.out.println("say hello"); }}Copy the code

Step 2

Create a proxy class that implements a MethodInterceptor interface similar to the InvocationHandler interface for JDK dynamic proxies.

public class HelloInterceptor implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before say hello");
        Methodproxy.invoke () cannot be used here because it overflows the stack, as explained below
        // 2. Method.invoke () cannot be invoked with o as an execution object, otherwise it will happen
        // Similar stack overflow error, you must create a new object, which will be explained later
        methodProxy.invokeSuper(o, objects);
        System.out.println("after say hello");
        return null; }}Copy the code

Step 3

public class Test {
    public static void main(String[] args) {
    
        // The proxy class can be generated by caching
        System.setProperty("cglib.useCache"."false");
        
        // Create an enhancer
        Enhancer enhancer = new Enhancer();
        
        // Set the class to be proxyinstance, equivalent to the second argument to newProxyInstance in JDK
        enhancer.setSuperclass(Hello.class);
        
        // Set the proxy class, equivalent to the third argument in the JDK to newProxyInstance
        enhancer.setCallback(new HelloInterceptor());

        // Create a dynamic proxy class
        Hello jack = (Hello) enhancer.create();
        
        // Call the methodjack.sayHello(); }}Copy the code

The execution result

Source code analysis

When analyzing the source code, I don’t think about caching first, because when you think about caching, the complexity is mostly in caching, which is a bit off topic. I’ll look at the source code with caching later.

enhancer.create()

This method creates a proxy class and returns an instance of that class, that is, an instance of a dynamic proxy class, using the no-argument construct of the proxied class (hello.class)

public Object create(a) {
    // Used to determine whether objects need to be created. False indicates that objects need to be created
    classOnly = false;
    // This property is null because we are using a no-parameter construct
    argumentTypes = null;
    // create a dynamic proxy class method
    return createHelper();
}
Copy the code

If the propped Class is an argument-constructed Class, you can use an overloaded create(Class[] argumentTypes, Object[] arguments), similar to the previous method, but with additional argumentTypes

// Accept two arguments
// 1. Array of parameter types
// 2. Specify an array of parameters
public Object create(Class[] argumentTypes, Object[] arguments) {
    classOnly = false;
    if (argumentTypes == null || arguments == null|| argumentTypes.length ! = arguments.length) {throw new IllegalArgumentException("Arguments must be non-null and of equal length");
    }
    this.argumentTypes = argumentTypes;
    this.arguments = arguments;
    return createHelper();
}
Copy the code

createHelper()

Create a unique key for the dynamic proxy class to be generated, use this key to represent the unique identity of the dynamic proxy class to be generated, and delegate creation to the parent class.

private Object createHelper(a) {
    // Check whether the type of CallBack is correct
    preValidate();
    // Create a unique identifierObject key = KEY_FACTORY.newInstance((superclass ! =null)? superclass.getName() :null,
            ReflectUtils.getNames(interfaces),
            filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
            callbackTypes,
            useFactory,
            interceptDuringConstruction,
            serialVersionUID);
    this.currentKey = key;
    // Delegate creation to the parent class
    Object result = super.create(key);
    return result;
}
Copy the code

super.create()

The method in AbstractClassGenerator is called. (if) the method will try to fetch from the cache

protected Object create(Object key) {
    try {
        // Get ClassLoader, usually AppClassLoader
        ClassLoader loader = getClassLoader();
        // The classloader is used as the key to cache the dynamic proxy classes loaded by the classloader
        Map<ClassLoader, ClassLoaderData> cache = CACHE;
        ClassLoaderData data = cache.get(loader);
        if (data == null) {
            synchronized (AbstractClassGenerator.class) {
                cache = CACHE;
                data = cache.get(loader);
                if (data == null) {
                    Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
                    data = newClassLoaderData(loader); newCache.put(loader, data); CACHE = newCache; }}}this.key = key;
        
        This is where we create and get dynamic proxy classes, because we don't use caching
        // So a dynamic proxy class will be created using ASM directly and loaded using ClassLoader
        // This returns a Class object.
        Object obj = data.get(this, getUseCache());
        
        // If a Class object is returned, create an instance of that Class
        if (obj instanceof Class) {
            return firstInstance((Class) obj);
        }
        
        // (only if caching is enabled) if one is returned
        // EnhancerFactoryData object, then a corresponding dynamic proxy Class object already exists
        return nextInstance(obj);
    } catch (RuntimeException e) {
        throw e;
    } catch (Error e) {
        throw e;
    } catch (Exception e) {
        throw newCodeGenerationException(e); }}Copy the code

data.get()

From the cache, since caching is not enabled, a dynamic proxy class will be created directly

public Object get(AbstractClassGenerator gen, boolean useCache) {
    // Here useCache is false, so it is created directly from the abstract class generator
    if(! useCache) {// Execute this sentence.
      // Passes an argument: classloaderData. this,
      // This parameter represents the ClassLoaderData object that executes this code, i.e., the data object of the title
      // Actually writing this directly is the same, since this method belongs to the inner class ClassLoaderData
      return gen.generate(ClassLoaderData.this);
    } else {
      Object cachedValue = generatedClasses.get(gen);
      returngen.unwrapCachedValue(cachedValue); }}Copy the code

gen.generate()

Generate a dynamic proxy class using an abstract class generator and load it using a class loader

protected Class generate(ClassLoaderData data) {
    Class gen;
    Object save = CURRENT.get();
    CURRENT.set(this);
    try {
        // Get the class load used to load the generated dynamic proxy classes
        ClassLoader classLoader = data.getClassLoader();
        if (classLoader == null) {
            throw new IllegalStateException("ClassLoader is null while trying to define class " +
                    getClassName() + ". It seems that the loader has been expired from a weak reference somehow. " +
                    "Please file an issue at cglib's issue tracker.");
        }
        synchronized (classLoader) {
          // Generate a dynamic proxy class name
          String name = generateClassName(data.getUniqueNamePredicate());
          data.reserveName(name);
          this.setClassName(name);
        }
        if (attemptLoad) {
            try {
                gen = classLoader.loadClass(getClassName());
                return gen;
            } catch (ClassNotFoundException e) {
                // ignore}}// Use ASM to generate class files for dynamic proxy classes
        // We will not analyze how to use ASM to generate class files
        // This requires familiarity with JVM structures and class file structures
        // Using ASM directly is also not recommended (unless you are familiar with the class file structure).
        byte[] b = strategy.generate(this);
        
        // Get the class name of the generated dynamic proxy class, which should be the same as the name above
        String className = ClassNameReader.getClassName(new ClassReader(b));
        ProtectionDomain protectionDomain = getProtectionDomain();
        synchronized (classLoader) { // just in case
            if (protectionDomain == null) {
                gen = ReflectUtils.defineClass(className, b, classLoader);
            } else {
                // Use the classloader to load the Class file and return a Class objectgen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain); }}// Returns the generated Class object
        return gen;
    } catch (RuntimeException e) {
        throw e;
    } catch (Error e) {
        throw e;
    } catch (Exception e) {
        throw new CodeGenerationException(e);
    } finally{ CURRENT.set(save); }}Copy the code

It then goes all the way back to the super.create() method to get a Class object for the dynamic proxy Class.

Because it is returning a Class object, the firstInstance() method is executed, which essentially gets the constructor from the Class object, creates an instance from the constructor, and returns the instance. I won’t post the code because it’s easier.

Dynamic proxy class

Output dynamic proxy classes

Just add a system property to the front of the test class

public static void main(String[] args) {
    // Outputs the generated dynamic proxy class
    // Second argument: output path
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "H:\\mybatis_demo");
    
    // Disable caching, default true
    System.setProperty("cglib.useCache"."false");

    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(Hello.class);
    enhancer.setCallback(new HelloInterceptor());
    
    Hello jack = (Hello) enhancer.create();
    jack.sayHello();
}
Copy the code

Analyze dynamic proxy classes

public class Hello?EnhancerByCGLIB? 35d006ca extends Hello implements Factory {
    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$sayHello$0$Method;
    private static final MethodProxy CGLIB$sayHello$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;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.lhd.cglib.Hello? EnhancerByCGLIB? 35d006ca");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals"."(Ljava/lang/Object;) Z"."toString"."()Ljava/lang/String;"."hashCode"."()I"."clone"."()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$equals$1$Method = var10000[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;) Z"."equals"."CGLIB$equals$1");
        CGLIB$toString$2$Method = var10000[1];
        CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;"."toString"."CGLIB$toString$2");
        CGLIB$hashCode$3$Method = var10000[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I"."hashCode"."CGLIB$hashCode$3");
        CGLIB$clone$4$Method = var10000[3];
        CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;"."clone"."CGLIB$clone$4");
        CGLIB$sayHello$0$Method = ReflectUtils.findMethods(new String[]{"sayHello"."()V"}, (var1 = Class.forName("com.lhd.cglib.Hello")).getDeclaredMethods())[0];
        CGLIB$sayHello$0$Proxy = MethodProxy.create(var1, var0, "()V"."sayHello"."CGLIB$sayHello$0");
    }

    final void CGLIB$sayHello$0() {
        super.sayHello();
    }

    public final void sayHello(a) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if(var10000 ! =null) {
            var10000.intercept(this, CGLIB$sayHello$0$Method, CGLIB$emptyArgs, CGLIB$sayHello$0$Proxy);
        } else {
            super.sayHello(); }}final boolean CGLIB$equals$1(Object var1) {
        return super.equals(var1);
    }

    public final boolean equals(Object var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if(var10000 ! =null) {
            Object var2 = var10000.intercept(this, CGLIB$equals$1$Method, new Object[]{var1}, CGLIB$equals$1$Proxy);
            return var2 == null ? false : (Boolean)var2;
        } else {
            return super.equals(var1); }}final String CGLIB$toString$2() {
        return super.toString();
    }

    public final String toString(a) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        returnvar10000 ! =null ? (String)var10000.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy) : super.toString();
    }

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

    public final int hashCode(a) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if(var10000 ! =null) {
            Object var1 = var10000.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
            return var1 == null ? 0 : ((Number)var1).intValue();
        } else {
            return super.hashCode(); }}final Object CGLIB$clone$4(a)throws CloneNotSupportedException {
        return super.clone();
    }

    protected final Object clone(a) throws CloneNotSupportedException {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        returnvar10000 ! =null ? var10000.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy) : super.clone();
    }

    public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
        String var10000 = var0.toString();
        switch(var10000.hashCode()) {
        case -508378822:
            if (var10000.equals("clone()Ljava/lang/Object;")) {
                return CGLIB$clone$4$Proxy;
            }
            break;
        case 1535311470:
            if (var10000.equals("sayHello()V")) {
                return CGLIB$sayHello$0$Proxy;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;) Z")) {
                return CGLIB$equals$1$Proxy;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return CGLIB$toString$2$Proxy;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return CGLIB$hashCode$3$Proxy; }}return null;
    }

    publicHello? EnhancerByCGLIB?35d006ca() {
        CGLIB$BIND_CALLBACKS(this);
    }

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

    public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
        CGLIB$STATIC_CALLBACKS = var0;
    }

    private static final voidCGLIB$BIND_CALLBACKS(Object var0) { Hello? EnhancerByCGLIB?35d006ca var1 = (Hello? EnhancerByCGLIB?35d006ca)var0;
        if(! var1.CGLIB$BOUND) { var1.CGLIB$BOUND =true;
            Object var10000 = CGLIB$THREAD_CALLBACKS.get();
            if (var10000 == null) {
                var10000 = CGLIB$STATIC_CALLBACKS;
                if (var10000 == null) {
                    return;
                }
            }

            var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0]; }}public Object newInstance(Callback[] var1) { CGLIB$SET_THREAD_CALLBACKS(var1); Hello? EnhancerByCGLIB?35d006ca var10000 = newHello? EnhancerByCGLIB?35d006ca();
        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Callback var1) {
        CGLIB$SET_THREAD_CALLBACKS(newCallback[]{var1}); Hello? EnhancerByCGLIB?35d006ca var10000 = newHello? EnhancerByCGLIB?35d006ca();
        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) { CGLIB$SET_THREAD_CALLBACKS(var3); Hello? EnhancerByCGLIB?35d006ca var10000 = newHello? EnhancerByCGLIB?35d006ca;
        switch(var1.length) {
        case 0:
            var10000.<init>();
            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
            return var10000;
        default:
            throw new IllegalArgumentException("Constructor not found"); }}public Callback getCallback(int var1) {
        CGLIB$BIND_CALLBACKS(this);
        MethodInterceptor var10000;
        switch(var1) {
        case 0:
            var10000 = this.CGLIB$CALLBACK_0;
            break;
        default:
            var10000 = null;
        }

        return var10000;
    }

    public void setCallback(int var1, Callback var2) {
        switch(var1) {
        case 0:
            this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
        default:}}public Callback[] getCallbacks() {
        CGLIB$BIND_CALLBACKS(this);
        return new Callback[]{this.CGLIB$CALLBACK_0};
    }

    public void setCallbacks(Callback[] var1) {
        this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
    }

    static{ CGLIB$STATICHOOK1(); }}Copy the code

From the output proxy class, we can see that the dynamic proxy class we output actually inherits from the proxy class and overwrites the methods in the proxy class. Because inheritance is used, final classes cannot be proxied, and final methods of non-final classes cannot be overwritten.

A dynamically generated proxy class is actually a subclass of the proxied class. Cglib overrides not only the parent methods, but also the toString(),hashCode(),equals(),clone(), wait(), and notify() methods of the Object class because they are final.

How do I implement proxy methods

When we execute the sayHello() method when we get the generated proxy class object, we actually execute the following code

public final void sayHello(a) {
    MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
    if (var10000 == null) {
        CGLIB$BIND_CALLBACKS(this);
        var10000 = this.CGLIB$CALLBACK_0;
    }

    if(var10000 ! =null) {
        var10000.intercept(this, CGLIB$sayHello$0$Method, CGLIB$emptyArgs, CGLIB$sayHello$0$Proxy);
    } else {
        super.sayHello(); }}Copy the code

When the variable of type MethodInterceptor (HelloInterceptor class, in this case) is not empty, the Intercept () method in that class is executed

Let’s analyze the parameters of this method:

  1. This: is the object of the dynamic proxy class
  2. CGLIB$sayHello$0$Method: this is a static attribute of type Method, whereCGLIB$STATICHOOK1()Method, which is called in the bottom static block of code. Is assigned to the Hello classsayHellomethods
  3. CGLIB$emptyArgs: Pass an array of parameters
  4. CGLIB$sayHello$0$Proxy: CGLIB creates a Proxy object for this method. It internally executes the original method via FastClass, which is faster than reflection, but with current ITERATIONS in the JDK, reflection is even more efficient.

Stack overflow problem

Stack overflow occurs when we call the methodProxy.invoke(O, objects) method. Let’s take a look inside how this method is implemented, okay

This method takes two arguments:

  1. The object that executes the method
  2. Method parameters
public Object invoke(Object obj, Object[] args) throws Throwable {
    try {
        // A FastClass class of the Hello class is created to call the specified method quickly
        // The index subscript is used to determine the method to execute. You can decompile to see if you are interested
        init();
        FastClassInfo fci = fastClassInfo;
        / / 1
        return fci.f1.invoke(fci.i1, obj, args);
    } catch (InvocationTargetException e) {
        throw e.getTargetException();
    } catch (IllegalArgumentException e) {
        if (fastClassInfo.i1 < 0)
            throw new IllegalArgumentException("Protected method: " + sig1);
        throwe; }}Copy the code

The line of code in 1 is the statement that actually executes the method. We can debug to see what the internal property is

f1
Hello? FastClassByCGLIB? 7572df32
invoke

When we execute the argument passed by this method, you can see that i1 is 0, in the yellow box.

We pass three parameters:

  1. 0
  2. Objects of the dynamic proxy class
  3. Parameters required to execute the method
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
    Hello var10000 = (Hello)var2;
    int var10001 = var1;

    try {
        switch(var10001) {
        case 0:
            var10000.sayHello();
            return null;
        case 1:
            return new Boolean(var10000.equals(var3[0]));
        case 2:
            return var10000.toString();
        case 3:
            return newInteger(var10000.hashCode()); }}catch (Throwable var4) {
        throw new InvocationTargetException(var4);
    }

    throw new IllegalArgumentException("Cannot find matching method/constructor");
}
Copy the code

SayHello () is the dynamic proxy object we passed in, which means jack.sayHello() is executed again; , and Jack.sayHello () will execute var10000.sayHello(), so keep calling each other until a stack overflow occurs.

A method that does not overflow the stack

After analyzing the last method call with stack overflow, let’s examine the rationale for method calls that don’t.

When we call methodProxy.invokeSuper(O, objects); , stack overflow will not occur, let’s take a look at the source of this method

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
        init();
        FastClassInfo fci = fastClassInfo;
        // Only this sentence is different
        return fci.f2.invoke(fci.i2, obj, args);
    } catch (InvocationTargetException e) {
        throwe.getTargetException(); }}Copy the code

f2
Hello? EnhancerByCGLIB? 35d006ca? FastClassByCGLIB? 518e6b66_3

We call the invoke method and pass the following arguments:

  1. 17
  2. Objects of the dynamic proxy class
  3. Parameters required to execute the method
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
    35d006ca var10000 = (35d006ca)var2;
    int var10001 = var1;

    try {
        switch(var10001) {
        case 0:
            return new Boolean(var10000.equals(var3[0]));
        case 1:
            return var10000.toString();
        case 2:
            return new Integer(var10000.hashCode());
        case 3:
            return var10000.clone();
        case 4:
            return var10000.newInstance((Callback[])var3[0]);
        case 5:
            return var10000.newInstance((Callback)var3[0]);
        case 6:
            return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
        case 7:
            var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
            return null;
        case 8:
            var10000.sayHello();
            return null;
        case 9:
            35d006ca.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
            return null;
        case 10:
            35d006ca.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
            return null;
        case 11:
            var10000.setCallbacks((Callback[])var3[0]);
            return null;
        case 12:
            return var10000.getCallbacks();
        case 13:
            return var10000.getCallback(((Number)var3[0]).intValue());
        case 14:
            return var10000.CGLIB$toString$2(a);case 15:
            return new Integer(var10000.CGLIB$hashCode$3());
        case 16:
            return var10000.CGLIB$clone$4(a);case 17:
            var10000.CGLIB$sayHello$0(a);return null;
        case 18:
            return new Boolean(var10000.CGLIB$equals$1(var3[0]));
        case 19:
            return 35d006ca.CGLIB$findMethodProxy((Signature)var3[0]);
        case 20:
            35d006ca.CGLIB$STATICHOOK1();
            return null; }}catch (Throwable var4) {
        throw new InvocationTargetException(var4);
    }

    throw new IllegalArgumentException("Cannot find matching method/constructor");
}
Copy the code

CGLIB$sayHello$0(); CGLIB$sayHello$0() {CGLIB$sayHello$0()

final void CGLIB$sayHello$0() {
    super.sayHello();
}
Copy the code

You’re essentially calling the parent’s sayHello method, which is the original one. So you don’t have to call each other.

There is one method. Invoke (O, objects) left unexplained. Invoke methodProxy.invoke()

conclusion

The essential differences between cglib dynamic proxies and JDK dynamic proxies are:

  • Cglib is inheritance-based for proxies of classes. Cglib implements dynamic proxies through inheritance (also through interfaces)
  • The JDK can only be based on interfaces because it needs to inherit Proxy classes itself, and Java does not support multiple inheritance. The JDK can only implement dynamic proxies by implementing interfaces.

Cglib dynamic proxy generates three Class files:

  1. A dynamic proxy class for the original class
  2. FastClass of the original class
  3. FastClass for dynamic proxy classes

Cglib calls the original method using the FastClass subscript. JDK dynamic proxies are called through reflection.

The end of the

This is the end of the cglib dynamic proxy, of course, ASM operation bytecode generation dynamic proxy file part because I am not familiar with it. However, it does not affect the understanding of the principle of CGLIb dynamic proxy. It took two days to write and 12 hours in total, plus debug, looking for information and reading source code, and finally finished. I hope you can forgive me if YOU see any mistakes. I will consult you humbly.