cglib create proxy object

MethodProxy call analysis

The beginning of the code starts with the example in the first article. I won't post the code. Look directly at MethodProxy

It is an input parameter in the MethodInterceptor, through which you can call the original method, the method of the parent class, or different objects of the same type.

The point is the last one. It can call different objects of the same type.

Method analysis

create(Class, Class, String, String, String)

    public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
        MethodProxy proxy = new MethodProxy();
        proxy.sig1 = new Signature(name1, desc);
        proxy.sig2 = new Signature(name2, desc);
        proxy.createInfo = new CreateInfo(c1, c2);
        return proxy;
    }

It is a static method used by Enhancer, through which MethodProxy can be created. The code in the corresponding proxy class is shown in the figure below

(this proxy class is an example of the initial test.).

It should be noted that it has several parameters: compare the above pictures

  1. Class c1: class object of the proxied class
  2. Class c2: class object of proxy class
  3. String desc: method description information (method parameters, exceptions)
  4. String name1: the name of the same method describing the information of the proxied class.
  5. String name2: the name of the same method describing the information of the proxy class.

Compare these input parameters to see the meaning of MethodProxy attribute:

Attribute interpretation

    private Signature sig1;
    private Signature sig2;
    private CreateInfo createInfo;
    
    private final Object initLock = new Object();
    private volatile FastClassInfo fastClassInfo;
  1. Signature sig1: method signature of the proxy class

  2. Signature sig2: method signature of proxy class

  3. CreateInfo createInfo: it is a context object for creating proxy objects. c1 represents the proxied class and c2 represents the proxy class. The remaining three are to obtain attribute values from the context in which the proxy object is currently created.

  1. FastClassInfo fastClassInfo: will be created later in the init method. It is the reference of the class created later through Signature and CreateInfo. It has two properties, one is the proxy class and the other is the proxy class.

  1. Object initLock: lock

invokeSuper

Call the parent method through MethodProxy.

Object obj: proxy class

Object []: the input parameter of the method.

In the init method, two subclasses of FastClass will be created (which will be analyzed below). Then, the invoke method of FastClass generated based on the proxy class will be called, which will make real method calls.

    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

Init method will be called for initialization. First look at the init method

init method

Create FastClassInfo objects through CreateInfo and Signature.

When creating FastClassInfo, two classes will be generated dynamically. They will inherit the FastClass object and assign it to the f1 and f2 attributes of FastClassInfo, and then find the subscript in the generated class through the method signature and assign it to the i1 and i2 attributes of FastClassInfo. This subscript corresponds to the value returned by the getIndex method in the generated FastClass.

That is, the f1 and f2 attributes of FastClassInfo are the FastClass objects generated by the proxy class and the proxy class. i1 and i2 save the subscripts in the respective FastClass objects of the calling method.

This index is the index returned by the getIndex method of the generated class. As for what this method looks like, continue to look down.

   private void init()
    {
        if (fastClassInfo == null)
        {
            synchronized (initLock)
            {   // double check
                if (fastClassInfo == null)
                {
                    CreateInfo ci = createInfo;

                    FastClassInfo fci = new FastClassInfo();
                    // Create the FastClass class and the class to be proxied
                    fci.f1 = helper(ci, ci.c1);
                    // Proxy class
                    fci.f2 = helper(ci, ci.c2);
                    // The method is called directly. The getIndex method of f1 is called. The method signature is the signature of the method in the proxy class.
                    fci.i1 = fci.f1.getIndex(sig1);
                     // same
                    fci.i2 = fci.f2.getIndex(sig2);
                    // assignment
                    fastClassInfo = fci;
                   // The purpose of null is to prevent re initialization. It's better to report mistakes directly than to make mistakes quietly.
                    createInfo = null;
                }
            }
        }
    }

The key point is the helper method. This method will generate classes through Cglib. It must have an internal class, inherit from AbstractClassGenerator, implement the generateClass method, and then write this class through the subclass of ClassEmitter.

Help method analysis

    private static FastClass helper(CreateInfo ci, Class type) {
        FastClass.Generator g = new FastClass.Generator();
        g.setType(type);
        g.setClassLoader(ci.c2.getClassLoader());
        g.setNamingPolicy(ci.namingPolicy);
        g.setStrategy(ci.strategy);
        g.setAttemptLoad(ci.attemptLoad);
        return g.create();
    }

Obviously, it inherits AbstractClassGenerator. I won't analyze how it is done. Because I don't quite understand. Finally, you can directly see what the generated FastClass looks like. You can see some things, such as Soruce, Key, 🤣.

invoke method

Like the above invokeSuper, it starts with init, but it calls the i but f1 invoke method FastClassInfo. (that is, the FastClass generated by the proxy class)

Through the above analysis, we know the meaning of FastClassInfo. FastClassInfo has four attributes. Two dynamic classes will be created in MethodProxy. These two classes are inherited from FastClass. The f1 and f2 attributes in FastClassInfo correspond to the reference of the FastClass generated by the proxy class and the proxy class. i1 and i2 correspond to the value returned by the getIndex method of the called method in the generated FastClass class.

  public Object invoke(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
           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);
            throw e;
        }
    }

In addition to the above two methods, MethodProxy has several methods, which I will also list and explain below.

  • getSignature: returns the signature of the proxy method of the proxy object
  • getSuperName: the method proxy object (generated through cglib) does not have the signature of the method directly calling super of the interceptor
  • getSuperIndex: returns the subscript returned through getIndex in the FastClass generated for the proxy object.
  • find: returns the method signature of the given type.

There are few opportunities for the daily use of these methods. The point is still the method mentioned above

At this point, what does the generated FastClass look like?

What does the generated FastClass look like?

  1. FastClass generated for the proxied class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package net.sf.cglib.samples.simple;

import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;

public class Bean$$FastClassByCGLIB$$62952930 extends FastClass {
    public Bean$$FastClassByCGLIB$$62952930(Class var1) {
        super(var1);
    }

    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case -1816210712:
            if (var10000.equals("sayHello(Ljava/lang/String;)Ljava/lang/String;")) {
                return 5;
            }
            break;
        case -1123095605:
            if (var10000.equals("hhhh()Ljava/lang/String;")) {
                return 3;
            }
            break;
        case -908043773:
            if (var10000.equals("sasasasa()Ljava/lang/String;")) {
                return 2;
            }
            break;
        case -286557062:
            if (var10000.equals("lipu1()Ljava/lang/String;")) {
                return 0;
            }
            break;
        case 580486146:
            if (var10000.equals("dadada()Ljava/lang/String;")) {
                return 6;
            }
            break;
        case 1631032358:
            if (var10000.equals("lululu()Ljava/lang/String;")) {
                return 1;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return 7;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return 8;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return 9;
            }
            break;
        case 2130387705:
            if (var10000.equals("jump()Ljava/lang/String;")) {
                return 4;
            }
        }

        return -1;
    }

    public int getIndex(String var1, Class[] var2) {
        switch(var1.hashCode()) {
        case -2012993625:
            if (var1.equals("sayHello")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("java.lang.String")) {
                        return 5;
                    }
                }
            }
            break;
        case -1776922004:
            if (var1.equals("toString")) {
                switch(var2.length) {
                case 0:
                    return 8;
                }
            }
            break;
        case -1339395145:
            if (var1.equals("dadada")) {
                switch(var2.length) {
                case 0:
                    return 6;
                }
            }
            break;
        case -1295482945:
            if (var1.equals("equals")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("java.lang.Object")) {
                        return 7;
                    }
                }
            }
            break;
        case -1091633701:
            if (var1.equals("lululu")) {
                switch(var2.length) {
                case 0:
                    return 1;
                }
            }
            break;
        case 3201536:
            if (var1.equals("hhhh")) {
                switch(var2.length) {
                case 0:
                    return 3;
                }
            }
            break;
        case 3273774:
            if (var1.equals("jump")) {
                switch(var2.length) {
                case 0:
                    return 4;
                }
            }
            break;
        case 102979631:
            if (var1.equals("lipu1")) {
                switch(var2.length) {
                case 0:
                    return 0;
                }
            }
            break;
        case 147696667:
            if (var1.equals("hashCode")) {
                switch(var2.length) {
                case 0:
                    return 9;
                }
            }
            break;
        case 2133693496:
            if (var1.equals("sasasasa")) {
                switch(var2.length) {
                case 0:
                    return 2;
                }
            }
        }

        return -1;
    }

    public int getIndex(Class[] var1) {
        switch(var1.length) {
        case 0:
            return 0;
        default:
            return -1;
        }
    }

    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        Bean var10000 = (Bean)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                return var10000.lipu1();
            case 1:
                return var10000.lululu();
            case 2:
                return var10000.sasasasa();
            case 3:
                return var10000.hhhh();
            case 4:
                return var10000.jump();
            case 5:
                return var10000.sayHello((String)var3[0]);
            case 6:
                return var10000.dadada();
            case 7:
                return new Boolean(var10000.equals(var3[0]));
            case 8:
                return var10000.toString();
            case 9:
                return new Integer(var10000.hashCode());
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

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

    public Object newInstance(int var1, Object[] var2) throws InvocationTargetException {
        Bean var10000 = new Bean;
        Bean var10001 = var10000;
        int var10002 = var1;

        try {
            switch(var10002) {
            case 0:
                var10001.<init>();
                return var10000;
            }
        } catch (Throwable var3) {
            throw new InvocationTargetException(var3);
        }

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

    public int getMaxIndex() {
        return 9;
    }
}

  1. FastClass generated for proxy class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package net.sf.cglib.samples.simple;

import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.samples.simple.Bean..EnhancerByCGLIB..d79f67a6;

public class Bean$$EnhancerByCGLIB$$d79f67a6$$FastClassByCGLIB$$cf2d2fec extends FastClass {
    public Bean$$EnhancerByCGLIB$$d79f67a6$$FastClassByCGLIB$$cf2d2fec(Class var1) {
        super(var1);
    }

    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case -2144987997:
            if (var10000.equals("CGLIB$lipu1$0()Ljava/lang/String;")) {
                return 18;
            }
            break;
        case -2055565910:
            if (var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
                return 11;
            }
            break;
        case -1816210712:
            if (var10000.equals("sayHello(Ljava/lang/String;)Ljava/lang/String;")) {
                return 16;
            }
            break;
        case -1457535688:
            if (var10000.equals("CGLIB$STATICHOOK1()V")) {
                return 20;
            }
            break;
        case -1123095605:
            if (var10000.equals("hhhh()Ljava/lang/String;")) {
                return 14;
            }
            break;
        case -908043773:
            if (var10000.equals("sasasasa()Ljava/lang/String;")) {
                return 13;
            }
            break;
        case -894172689:
            if (var10000.equals("newInstance(Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
                return 3;
            }
            break;
        case -623122092:
            if (var10000.equals("CGLIB$findMethodProxy(Lnet/sf/cglib/core/Signature;)Lnet/sf/cglib/proxy/MethodProxy;")) {
                return 19;
            }
            break;
        case -419626537:
            if (var10000.equals("setCallbacks([Lnet/sf/cglib/proxy/Callback;)V")) {
                return 6;
            }
            break;
        case -286557062:
            if (var10000.equals("lipu1()Ljava/lang/String;")) {
                return 7;
            }
            break;
        case 560567118:
            if (var10000.equals("setCallback(ILnet/sf/cglib/proxy/Callback;)V")) {
                return 8;
            }
            break;
        case 580486146:
            if (var10000.equals("dadada()Ljava/lang/String;")) {
                return 17;
            }
            break;
        case 811063227:
            if (var10000.equals("newInstance([Ljava/lang/Class;[Ljava/lang/Object;[Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
                return 5;
            }
            break;
        case 973717575:
            if (var10000.equals("getCallbacks()[Lnet/sf/cglib/proxy/Callback;")) {
                return 10;
            }
            break;
        case 1221173700:
            if (var10000.equals("newInstance([Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
                return 4;
            }
            break;
        case 1230699260:
            if (var10000.equals("getCallback(I)Lnet/sf/cglib/proxy/Callback;")) {
                return 9;
            }
            break;
        case 1584330438:
            if (var10000.equals("CGLIB$SET_STATIC_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
                return 12;
            }
            break;
        case 1631032358:
            if (var10000.equals("lululu()Ljava/lang/String;")) {
                return 21;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return 0;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return 1;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return 2;
            }
            break;
        case 2130387705:
            if (var10000.equals("jump()Ljava/lang/String;")) {
                return 15;
            }
        }

        return -1;
    }

    public int getIndex(String var1, Class[] var2) {
        switch(var1.hashCode()) {
        case -2012993625:
            if (var1.equals("sayHello")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("java.lang.String")) {
                        return 16;
                    }
                }
            }
            break;
        case -1776922004:
            if (var1.equals("toString")) {
                switch(var2.length) {
                case 0:
                    return 1;
                }
            }
            break;
        case -1339395145:
            if (var1.equals("dadada")) {
                switch(var2.length) {
                case 0:
                    return 17;
                }
            }
            break;
        case -1295482945:
            if (var1.equals("equals")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("java.lang.Object")) {
                        return 0;
                    }
                }
            }
            break;
        case -1091633701:
            if (var1.equals("lululu")) {
                switch(var2.length) {
                case 0:
                    return 21;
                }
            }
            break;
        case -1053468136:
            if (var1.equals("getCallbacks")) {
                switch(var2.length) {
                case 0:
                    return 10;
                }
            }
            break;
        case -60403779:
            if (var1.equals("CGLIB$SET_STATIC_CALLBACKS")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("[Lnet.sf.cglib.proxy.Callback;")) {
                        return 12;
                    }
                }
            }
            break;
        case 3201536:
            if (var1.equals("hhhh")) {
                switch(var2.length) {
                case 0:
                    return 14;
                }
            }
            break;
        case 3273774:
            if (var1.equals("jump")) {
                switch(var2.length) {
                case 0:
                    return 15;
                }
            }
            break;
        case 85179481:
            if (var1.equals("CGLIB$SET_THREAD_CALLBACKS")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("[Lnet.sf.cglib.proxy.Callback;")) {
                        return 11;
                    }
                }
            }
            break;
        case 102979631:
            if (var1.equals("lipu1")) {
                switch(var2.length) {
                case 0:
                    return 7;
                }
            }
            break;
        case 147696667:
            if (var1.equals("hashCode")) {
                switch(var2.length) {
                case 0:
                    return 2;
                }
            }
            break;
        case 161998109:
            if (var1.equals("CGLIB$STATICHOOK1")) {
                switch(var2.length) {
                case 0:
                    return 20;
                }
            }
            break;
        case 495524492:
            if (var1.equals("setCallbacks")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("[Lnet.sf.cglib.proxy.Callback;")) {
                        return 6;
                    }
                }
            }
            break;
        case 1154623345:
            if (var1.equals("CGLIB$findMethodProxy")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("net.sf.cglib.core.Signature")) {
                        return 19;
                    }
                }
            }
            break;
        case 1264770776:
            if (var1.equals("CGLIB$lipu1$0")) {
                switch(var2.length) {
                case 0:
                    return 18;
                }
            }
            break;
        case 1811874389:
            if (var1.equals("newInstance")) {
                switch(var2.length) {
                case 1:
                    String var10001 = var2[0].getName();
                    switch(var10001.hashCode()) {
                    case -845341380:
                        if (var10001.equals("net.sf.cglib.proxy.Callback")) {
                            return 3;
                        }
                        break;
                    case 1730110032:
                        if (var10001.equals("[Lnet.sf.cglib.proxy.Callback;")) {
                            return 4;
                        }
                    }
                case 2:
                default:
                    break;
                case 3:
                    if (var2[0].getName().equals("[Ljava.lang.Class;") && var2[1].getName().equals("[Ljava.lang.Object;") && var2[2].getName().equals("[Lnet.sf.cglib.proxy.Callback;")) {
                        return 5;
                    }
                }
            }
            break;
        case 1817099975:
            if (var1.equals("setCallback")) {
                switch(var2.length) {
                case 2:
                    if (var2[0].getName().equals("int") && var2[1].getName().equals("net.sf.cglib.proxy.Callback")) {
                        return 8;
                    }
                }
            }
            break;
        case 1905679803:
            if (var1.equals("getCallback")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("int")) {
                        return 9;
                    }
                }
            }
            break;
        case 2133693496:
            if (var1.equals("sasasasa")) {
                switch(var2.length) {
                case 0:
                    return 13;
                }
            }
        }

        return -1;
    }

    public int getIndex(Class[] var1) {
        switch(var1.length) {
        case 0:
            return 0;
        default:
            return -1;
        }
    }

    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        d79f67a6 var10000 = (d79f67a6)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.newInstance((Callback)var3[0]);
            case 4:
                return var10000.newInstance((Callback[])var3[0]);
            case 5:
                return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
            case 6:
                var10000.setCallbacks((Callback[])var3[0]);
                return null;
            case 7:
                return var10000.lipu1();
            case 8:
                var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
                return null;
            case 9:
                return var10000.getCallback(((Number)var3[0]).intValue());
            case 10:
                return var10000.getCallbacks();
            case 11:
                d79f67a6.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
                return null;
            case 12:
                d79f67a6.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
                return null;
            case 13:
                return var10000.sasasasa();
            case 14:
                return var10000.hhhh();
            case 15:
                return var10000.jump();
            case 16:
                return var10000.sayHello((String)var3[0]);
            case 17:
                return var10000.dadada();
            case 18:
                return var10000.CGLIB$lipu1$0();
            case 19:
                return d79f67a6.CGLIB$findMethodProxy((Signature)var3[0]);
            case 20:
                d79f67a6.CGLIB$STATICHOOK1();
                return null;
            case 21:
                return var10000.lululu();
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

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

    public Object newInstance(int var1, Object[] var2) throws InvocationTargetException {
        d79f67a6 var10000 = new d79f67a6;
        d79f67a6 var10001 = var10000;
        int var10002 = var1;

        try {
            switch(var10002) {
            case 0:
                var10001.<init>();
                return var10000;
            }
        } catch (Throwable var3) {
            throw new InvocationTargetException(var3);
        }

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

    public int getMaxIndex() {
        return 21;
    }
}

explain

FastClass itself is an abstract class. The generated subclasses implement its abstract methods, mainly including the following methods

  1. getIndex:

    In this method, all its methods will be obtained through the type to be created passed in to form a large switch and case. The hash value of the signature of the method passed in returns the corresponding subscript, which is the same as that used in invoke. See the public int getIndex(Signature var1) method above for details

  1. invoke

    Let's first look at the input parameters of his method:

int var1: indicates the passed index, which is returned by getIndex.

Object var2: indicates the object to be called.

Object[] var3: input parameter of method.

From the code point of view, it is very simple. First, it is forced to the type of call (such as (Bean) var2 above), and then the specific method is called through the input parameter (VaR, actually index).

The proxy class and the proxied class have the same logic, but the types are inconsistent during forced conversion, and other operations are consistent.

summary

When building a proxy class, if the interceptor type is MethodIntercept, a MethodProxy for the method used by MethodIntercept will be created. When calling MethodIntercept, the MethodProxy will be passed. MethodProxy is initialized in the static code block of the generated proxy class. (through the MethodProxy#create method).

When creating MethodProxyd, four parameters will be passed, which are corresponding to the class object of the proxy class object, the class object of the proxy class object, and the Signature of the method in which MethodIntercepted works. This method corresponds to the name in the proxy class and proxy class. Assign values to several properties of MethodProxy through these parameters, and they also have different semantics. Cglib uses Signature to represent the Signature of a method (method name and method description information), corresponding to the sig1 (method Signature of the proxy class) and sig2 (method Signature of the proxy class) properties of MethodProxy.

In addition, MethodProxy also has a FastClassInfo attribute. When calling MethodProxy#invoke or invokeSuper in Cglib, two subclasses of FastClass will be dynamically generated for sig1 and sig2. In this subclass, all methods of the type to be implemented will be obtained and called by these methods. There are mainly two methods in the generated subclass of FastClass, getIndex and invoke methods. In getIndex, the method signature will be used to return the index. These indexes correspond to the call subscript of the method in invoke.

In the invoke method, the object passed in is forcibly converted to the class object of the method type, and the method call is made through the subscript.

In short, when calling, MethodProxy will dynamically generate two classes inherited from FastClass. There are two important methods, getindex and invoke methods. In the class, all the methods of the class to be implemented will be obtained. These methods will form the subscript of method call starting from 0 in invoke. In addition, the object passed in by the invoke method will be forcibly converted to the type of the class to be implemented, and method calls will be made through subscript matching. In the getindex method, match the hashcode signed by the passed method and return the index (this index is the index in the corresponding invoke method).

You can see that in the invoke method, the generated FastClass is mainly passed in, and the subclasses of the class to be implemented are all OK. There is a forced rotation operation in invoke.

Method call analysis

The properties of MethodProxy and the function of FastClass are analyzed above. This chapter focuses on method invocation. The method call mentioned here is called through MethodIntercepted. Through the previous articles and previous analysis, it is known that the remaining child interfaces of callback do not call the operation of the parent class. There is no MethodProxy. Let's start with an example.

Let's take another example. Look at this example. There is a callback (MethodInterceptor) with TestBean and TestBean1. The relationship between the two is generalized. In addition, the subclass overrides the test1 method of the parent class. There are nested calls in TestBean.

The following is an analysis of the different calling methods of invokeSupe and invoke.

public class TestMain {
    public static void main(String[] args) {
        final TestBean testBean = new TestBean();
        final  TestBean1 testBean1 = new TestBean1();

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(TestBean.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("intercept:" + method.getName());
               return proxy.invokeSuper(obj,args);
            }
        });
        TestBean o = (TestBean)enhancer.create();
        System.out.println(o.test("param"));
    }
}
class TestBean{

    public String test(String param){
        String s = test1(param);
        return "test:" + s;
    }

    public String test1(String param){
        return "test1:" + param;
    }
}
class TestBean1 extends TestBean{
    @Override
    public String test1(String param) {
        return "TestBean1:test1";
    }
}

invokeSuper

result

Nested calls to methods were found.

analysis:

Think about the FastClass object generated above. When invokeSuper, the invoke method of MethodProxy#FastClassInfo#f2 is called

MethodProxy is passed when it is created in the proxy class.

It can be seen from this that getIndex passes CGLIB$test1 . When invokeSuper, it will call the invoke method in the dynamically generated FastClass, which will be forcibly converted into a proxy class, and finally call the CGLIB$test1 method. Therefore, this explains that the proxy object is passed in invokeSuper, because the proxy object can be converted into a proxy object.

Finally, it will call CGLIB$test1 (this method is automatically generated by Cglib. As long as the methodinterceptor referenced by this method, there will be one more MethodProxy and one method without methodinterceptor, and the super method will be called directly in this method.)

The methodinterceptor method is not called in CGLIB$test1 . Because she cannot call, if it is called, a circular call will occur, and the callback will be called all the time, resulting in stack overflow.

Call to the parent class of the proxy class. The test1 method is called again in the parent class, and the test1 method also has a proxy. Therefore, go to the above logic again and invoke the method of the parent class again.

invokeSuper can detect nested calls before methods.

invoke

  1. The invoke method passes parameters as proxy objects

    The stack overflows because:

    When invoking invoke, it calls the invoke method in the fastClass generated by the proxy class.

The proxy class can certainly be converted to TestBean and directly call the test method of the proxy class. This is no different from direct calling. It will still be called into the proxy class or methodInterceptor. It will continue to cycle.

  1. The invoke method passes parameters to ordinary objects

The call is normal, because in the invoke method of fastclass generated for the proxy class, it will be forcibly converted to testbean type, and the testbean passed in is testbean. Direct strong conversion in invoke, and then call directly. Therefore, there is no problem (this is the way in which the Spring facet is called, which leads to the so-called failure, which is the essential reason)

  1. The invoke method passes parameters as subclasses of the proxied object

    [the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-ZAcC3Vqn-

    Note that TestBean 1 inherits TestBean, so it can also be forcibly converted to TestBean type in the invoke method, but TestBean 1 overrides test1 method. Therefore, the method of the subclass is called when it is called. This calling method is consistent with the original meaning of FastClass#invoke.

summary

When MethodProxy calls invokeSuper, calls between methods can be found. When invoking invoke, the nesting between methods cannot be found.

In addition, when calling MethodProxy, two subclasses of FastClass will be dynamically created for the proxy class and the proxied class. When invoking and invoke super, methods in different FastClass classes will be called.

This concludes the discussion on Cglib. It mainly analyzes the internal process of creating proxy classes in Cglib and several interesting attributes. These attributes are used for caching and generation of proxy object class names. It also analyzes various subclasses of Callback, their use in Cglib, and how Cglib writes these methods. In addition. It also analyzes the code logic related to the call of MethodProxy in methodinterceptor. The problem of invalidation when Cglib creates proxy objects is analyzed from the root (mainly divided into two types: invokeSuper will not and invoke will).


    private void init()
    {
        /* 
         * Using a volatile invariant allows us to initialize the FastClass and
         * method index pairs atomically.
         * 
         * Double-checked locking is safe with volatile in Java 5.  Before 1.5 this 
         * code could allow fastClassInfo to be instantiated more than once, which
         * appears to be benign.
         */
        if (fastClassInfo == null)
        {
            synchronized (initLock)
            {
                if (fastClassInfo == null)
                {
                    CreateInfo ci = createInfo;

                    FastClassInfo fci = new FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(sig1);
                    fci.i2 = fci.f2.getIndex(sig2);
                    fastClassInfo = fci;
                    createInfo = null;
                }
            }
        }
    }

 
    private static FastClass helper(CreateInfo ci, Class type) {
        FastClass.Generator g = new FastClass.Generator();
        g.setType(type);
        g.setClassLoader(ci.c2.getClassLoader());
        g.setNamingPolicy(ci.namingPolicy);
        g.setStrategy(ci.strategy);
        g.setAttemptLoad(ci.attemptLoad);
        return g.create();
    }

    private MethodProxy() {
    }

    /**
     * Return the signature of the proxied method.
     */
    public Signature getSignature() {
        return sig1;
    }

    /**
     * Return the name of the synthetic method created by CGLIB which is
     * used by {@link #invokeSuper} to invoke the superclass
     * (non-intercepted) method implementation. The parameter types are
     * the same as the proxied method.
     */
    public String getSuperName() {
        return sig2.getName();
    }

    /**
     * Return the {@link net.sf.cglib.reflect.FastClass} method index
     * for the method used by {@link #invokeSuper}. This index uniquely
     * identifies the method within the generated proxy, and therefore
     * can be useful to reference external metadata.
     * @see #getSuperName
     */
    public int getSuperIndex() {
        init();
        return fastClassInfo.i2;
    }

    // For testing
    FastClass getFastClass() {
      init();
      return fastClassInfo.f1;
    }

    // For testing
    FastClass getSuperFastClass() {
      init();
      return fastClassInfo.f2;
    }

    /**
     * Return the <code>MethodProxy</code> used when intercepting the method
     * matching the given signature.
     * @param type the class generated by Enhancer
     * @param sig the signature to match
     * @return the MethodProxy instance, or null if no applicable matching method is found
     * @throws IllegalArgumentException if the Class was not created by Enhancer or does not use a MethodInterceptor
     */
    public static MethodProxy find(Class type, Signature sig) {
        try {
            Method m = type.getDeclaredMethod(MethodInterceptorGenerator.FIND_PROXY_NAME,
                                              MethodInterceptorGenerator.FIND_PROXY_TYPES);
            return (MethodProxy)m.invoke(null, new Object[]{ sig });
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Class " + type + " does not use a MethodInterceptor");
        } catch (IllegalAccessException e) {
            throw new CodeGenerationException(e);
        } catch (InvocationTargetException e) {
            throw new CodeGenerationException(e);
        }
    }

    /**
     * Invoke the original method, on a different object of the same type.
     * @param obj the compatible object; recursion will result if you use the object passed as the first
     * argument to the MethodInterceptor (usually not what you want)
     * @param args the arguments passed to the intercepted method; you may substitute a different
     * argument array as long as the types are compatible
     * @see MethodInterceptor#intercept
     * @throws Throwable the bare exceptions thrown by the called method are passed through
     * without wrapping in an <code>InvocationTargetException</code>
     */
    public Object invoke(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
                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);
            throw e;
        }
    }

    /**
     * Invoke the original (super) method on the specified object.
     * @param obj the enhanced object, must be the object passed as the first
     * argument to the MethodInterceptor
     * @param args the arguments passed to the intercepted method; you may substitute a different
     * argument array as long as the types are compatible
     * @see MethodInterceptor#intercept
     * @throws Throwable the bare exceptions thrown by the called method are passed through
     * without wrapping in an <code>InvocationTargetException</code>
     */
    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }
}

That's it. It's over.

About the blog, I take it as my notes. There are a lot of contents in it that reflect my thinking process. Because my thinking is limited, there are some differences in some contents. If there are any problems, please point them out. Discuss together. thank you.

Keywords: Spring

Added by englishman69 on Fri, 24 Dec 2021 18:24:15 +0200