Introduction to the

Cglib is another dynamic proxy method, which is different from the JDK dynamic proxy implementation. We have seen that JDK dynamic proxy class must implement the interface, while Cglib does not need to implement the interface, but must ensure that the class does not contain the final keyword, otherwise it will not be able to proxy. This paper is based on the analysis of the cGLIB dead-loop problem accidentally encountered by individuals.

Additional case

Let’s show an example of a cglib loop. First, the proxied class declares its own methods, as usual, but makes sure that the class and method are not decorated with the final keyword. Modifying a class with the final keyword directly raises an exception, but the modifying method does not throw an exception, but this method is not proxied, but does not affect other methods being proxied.

public class InfoDemo {
    public void welcome (String person){
        System.out.println("welcome :"+ person); }}Copy the code

The following is a concrete proxy class implementation

public class CglibInfoProxy implements MethodInterceptor{
    private Object target;
    public Object newInstance(Object source){
        target = source;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before method!!!");
        Object value = methodProxy.invoke(o, objects);
        //Object value = methodProxy.invoke(this.target, objects);
        //Object value = methodProxy.invokeSuper(o, objects);
        return value;
    }
   public static void main(String[] args) {
        //System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\\\classes");
      InfoDemo instance = (InfoDemo) new CglibInfoProxy().newInstance(new InfoDemo());
      instance.welcome("zhangsan"); }}Copy the code

This looks very similar to our JDK dynamic proxy, except that the two classes implement different interfaces and use different methods to generate objects. The difference between invoke and invokeSuper is that invoke must use the propped object (target), while invokeSuper must use the propped object (O).

This example can cause an infinite loop that causes StackOverflowFlow to overflow

Specific why this happens, you can think about it first, and then we will explain it in the source code implementation. Now let’s take a look at the results

. before method!!! before method!!! before method!!! Exception in thread "main" java.lang.StackOverflowError at java.nio.CharBuffer.<init>(CharBuffer.java:281) at java.nio.HeapCharBuffer.<init>(HeapCharBuffer.java:70) at java.nio.CharBuffer.wrap(CharBuffer.java:373) at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265) at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125) at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207) at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129) at java.io.PrintStream.write(PrintStream.java:526) at java.io.PrintStream.print(PrintStream.java:669) at java.io.PrintStream.println(PrintStream.java:806) at com.eumji.proxy.cglib.CglibInfoProxy.intercept(CglibInfoProxy.java:30) at com.eumji.proxy.cglib.InfoDemo?EnhancerByCGLIB?870a84d7.welcome(<generated>) at com.eumji.proxy.cglib.InfoDemo?FastClassByCGLIB?2e560a7d.invoke(<generated>) at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ...Copy the code

Here only show partial effect, specific can try yourself.

If we replace the other two statements with the correct output, the results are as follows

before method!!!
welcome :zhangsan
Copy the code

The principle of analytic

To find out what is going on, first we need to look at the cglib proxied class. To generate the cglib proxied class file, we need to uncomment this method in our main method

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, “D:\\classes”);

The proxy classes will be generated in the CLASSES file on disk D. Note that there are three generated proxy classes. We will first introduce the InfoDemo proxy class that we are most interested in, and the other two will be described later at an appropriate time.

InfoDemo decompiles the code

Drag the class file directly into IDEA

public class InfoDemo?EnhancerByCGLIB? 870a84d7 extends InfoDemo implements Factory {
    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$welcome$0$Method;
    private static final MethodProxy CGLIB$welcome$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$finalize$1$Method;
    private static final MethodProxy CGLIB$finalize$1$Proxy;
    private static final Method CGLIB$equals$2$Method;
    private static final MethodProxy CGLIB$equals$2$Proxy;
    private static final Method CGLIB$toString$3$Method;
    private static final MethodProxy CGLIB$toString$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;

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

    final void CGLIB$welcome$0(String var1) {
        super.welcome(var1);
    }

    public final void welcome(String var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if(var10000 ! =null) {
            var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy);
        } else {
            super.welcome(var1); }}... }Copy the code

Cglib generates two welcome methods for the propped class. The welcome method and the cglib $welcome$0 method are both welcome methods and welcome methods

1. The CGLIB$welcome$0 method calls the propped method directly, i.e. does nothing.

The welcome method checks whether callback is set to CglibInfoProxy, so cglibInfoProxy.Intercept is called.

I want to analyze the process of generating a wave of proxy classes, but it is a bit complicated, so I will not analyze it for the moment.

InvokeSuper method

As mentioned above, the invoke and invokeSuper methods will go wrong if they are not noticed. Here we will trace the cause of the problem from the code level.

Let’s look at the execution flow of the proxy class method invokeSuper

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
        this.init(); // Initialize fastInfo
        MethodProxy.FastClassInfo fci = this.fastClassInfo;
        return fci.f2.invoke(fci.i2, obj, args);
    } catch (InvocationTargetException var4) {
        throwvar4.getTargetException(); }}Copy the code

The main function of invokeSuper here is to initialize fastClassInfo.

The init method

private void init(a) {
    if (this.fastClassInfo == null) {
        Object var1 = this.initLock;
        synchronized(this.initLock) {
            if (this.fastClassInfo == null) {
                MethodProxy.CreateInfo ci = this.createInfo;
                MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                fci.f1 = helper(ci, ci.c1);
                fci.f2 = helper(ci, ci.c2);
                fci.i1 = fci.f1.getIndex(this.sig1);
                fci.i2 = fci.f2.getIndex(this.sig2);
                this.fastClassInfo = fci;
                this.createInfo = null; }}}}Copy the code

The above method is mainly loaded methodProxy FastClassInfo. Ci is initialized before good, c1 refers to the proxy class InfoDemo, c2 is com eumji. Proxy. Additional. InfoDemo? EnhancerByCGLIB? Efe38465 this proxy class.

The welcome method and the CGLIB$welcome$0 method are used to generate the corresponding f1 and f2 and the subscripts i1 and i2 of the methods.

And f1 is InfoDemo? FastClassByCGLIB? 2e560a7d proxy class, f2 corresponding to InfoDemo? EnhancerByCGLIB? efe38465? FastClassByCGLIB? 38345933 Proxy class. These can be viewed in the generated proxy class.

Of course, it’s not clear what’s generated here, but you can see for yourself how bytecode is generated. Personally, I don’t understand.

Invoke is different from invokeSuper

The invoke method and the invokeSuper method are mentioned above. Let’s compare the invoke method and the invokeSuper method

public Object invoke(Object obj, Object[] args) throws Throwable {
  this.init();
  MethodProxy.FastClassInfo fci = this.fastClassInfo;
  return fci.f1.invoke(fci.i1, obj, args);
}
Copy the code

We can see that invoke uses the F1. invoke method, while invokeSuper uses the F2. invoke method.

First, take a look at the invoke method logic corresponding to F1

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

        try {
            switch(var10001) {
            case 0:
                var10000.welcome((String)var3[0]);
                return null; . }Copy the code

Call the Welcome method of the InfoDemo object directly.

This explains why we looped the Invoke method earlier, since the var2 we passed was a proxy object for InfoDemo, and as you can see from the first proxy class code, we went back to the Invoke method, causing an infinite loop.

Look again at the implementation of the corresponding Invoke in F2

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

        try {
            switch(var10001) {
             ....
                var10000.CGLIB$finalize$1(a);return null;
            case 16:
                var10000.CGLIB$welcome$0((String)var3[0]);
                return null; . }Copy the code

Because the var2 we pass in at this point is the InfoDemo proxy object, we end up calling the CGLIB$welcome$0 method in the proxy class.

summary

Cglib is much more complex than JDK dynamic proxy, mainly in the logic of generated code and generated code, which needs further study.

Furthermore, there are two invoke methods: invoke and invokeSuper, so use them with caution.

conclusion

This article is from personal notes, if there is any improper expression or mistake, welcome to correct.

With your mutual encouragement!!

Introduction to the

Cglib is another dynamic proxy method, which is different from the JDK dynamic proxy implementation. We have seen that JDK dynamic proxy class must implement the interface, while Cglib does not need to implement the interface, but must ensure that the class does not contain the final keyword, otherwise it will not be able to proxy.

Additional case

Let’s show an example of a cglib loop. First, the proxied class declares its own methods, as usual, but makes sure that the class and method are not decorated with the final keyword. Modifying a class with the final keyword directly raises an exception, but the modifying method does not throw an exception, but this method is not proxied, but does not affect other methods being proxied.

public class InfoDemo {
    public void welcome (String person){
        System.out.println("welcome :"+ person); }}Copy the code

The following is a concrete proxy class implementation

public class CglibInfoProxy implements MethodInterceptor{
    private Object target;
    public Object newInstance(Object source){
        target = source;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before method!!!");
        Object value = methodProxy.invoke(o, objects);
        //Object value = methodProxy.invoke(this.target, objects);
        //Object value = methodProxy.invokeSuper(o, objects);
        return value;
    }
   public static void main(String[] args) {
        //System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\\\classes");
      InfoDemo instance = (InfoDemo) new CglibInfoProxy().newInstance(new InfoDemo());
      instance.welcome("zhangsan"); }}Copy the code

This looks very similar to our JDK dynamic proxy, except that the two classes implement different interfaces and use different methods to generate objects. The difference between invoke and invokeSuper is that invoke must use the propped object (target), while invokeSuper must use the propped object (O).

This example can cause an infinite loop that causes StackOverflowFlow to overflow

Specific why this happens, you can think about it first, and then we will explain it in the source code implementation. Now let’s take a look at the results

. before method!!! before method!!! before method!!! Exception in thread "main" java.lang.StackOverflowError at java.nio.CharBuffer.<init>(CharBuffer.java:281) at java.nio.HeapCharBuffer.<init>(HeapCharBuffer.java:70) at java.nio.CharBuffer.wrap(CharBuffer.java:373) at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265) at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125) at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207) at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129) at java.io.PrintStream.write(PrintStream.java:526) at java.io.PrintStream.print(PrintStream.java:669) at java.io.PrintStream.println(PrintStream.java:806) at com.eumji.proxy.cglib.CglibInfoProxy.intercept(CglibInfoProxy.java:30) at com.eumji.proxy.cglib.InfoDemo?EnhancerByCGLIB?870a84d7.welcome(<generated>) at com.eumji.proxy.cglib.InfoDemo?FastClassByCGLIB?2e560a7d.invoke(<generated>) at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ...Copy the code

Here only show partial effect, specific can try yourself.

If we replace the other two statements with the correct output, the results are as follows

before method!!!
welcome :zhangsan
Copy the code

The principle of analytic

To find out what is going on, first we need to look at the cglib proxied class. To generate the cglib proxied class file, we need to uncomment this method in our main method

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, “D:\\classes”);

The proxy classes will be generated in the CLASSES file on disk D. Note that there are three generated proxy classes. We will first introduce the InfoDemo proxy class that we are most interested in, and the other two will be described later at an appropriate time.

InfoDemo decompiles the code

Drag the class file directly into IDEA

public class InfoDemo?EnhancerByCGLIB? 870a84d7 extends InfoDemo implements Factory {
    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$welcome$0$Method;
    private static final MethodProxy CGLIB$welcome$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$finalize$1$Method;
    private static final MethodProxy CGLIB$finalize$1$Proxy;
    private static final Method CGLIB$equals$2$Method;
    private static final MethodProxy CGLIB$equals$2$Proxy;
    private static final Method CGLIB$toString$3$Method;
    private static final MethodProxy CGLIB$toString$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;

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

    final void CGLIB$welcome$0(String var1) {
        super.welcome(var1);
    }

    public final void welcome(String var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if(var10000 ! =null) {
            var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy);
        } else {
            super.welcome(var1); }}... }Copy the code

Cglib generates two welcome methods for the propped class. The welcome method and the cglib $welcome$0 method are both welcome methods and welcome methods

1. The CGLIB$welcome$0 method calls the propped method directly, i.e. does nothing.

The welcome method checks whether callback is set to CglibInfoProxy, so cglibInfoProxy.Intercept is called.

I want to analyze the process of generating a wave of proxy classes, but it is a bit complicated, so I will not analyze it for the moment.

InvokeSuper method

As mentioned above, the invoke and invokeSuper methods will go wrong if they are not noticed. Here we will trace the cause of the problem from the code level.

Let’s look at the execution flow of the proxy class method invokeSuper

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
        this.init(); // Initialize fastInfo
        MethodProxy.FastClassInfo fci = this.fastClassInfo;
        return fci.f2.invoke(fci.i2, obj, args);
    } catch (InvocationTargetException var4) {
        throwvar4.getTargetException(); }}Copy the code

The main function of invokeSuper here is to initialize fastClassInfo and then call the target method through fastClassInfo.

The init method

This method is also critical because the proxy classes F1 and F2 are generated.

private void init(a) {
    if (this.fastClassInfo == null) {
        Object var1 = this.initLock;
        synchronized(this.initLock) {
            if (this.fastClassInfo == null) {
                MethodProxy.CreateInfo ci = this.createInfo;
                MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                fci.f1 = helper(ci, ci.c1); // FastClass proxy class generation
                fci.f2 = helper(ci, ci.c2);// FastClass proxy class generation
                fci.i1 = fci.f1.getIndex(this.sig1); // get the subscript
                fci.i2 = fci.f2.getIndex(this.sig2);
                this.fastClassInfo = fci;
                this.createInfo = null; }}}}Copy the code

The above method is mainly loaded methodProxy FastClassInfo. Ci is initialized before good, c1 refers to the proxy class InfoDemo, c2 is com eumji. Proxy. Additional. InfoDemo? EnhancerByCGLIB? Efe38465 this proxy class.

The welcome method and the CGLIB$welcome$0 method are used to generate the corresponding f1 and f2 and the subscripts i1 and i2 of the methods.

And f1 is InfoDemo? FastClassByCGLIB? 2e560a7d proxy class, f2 corresponding to InfoDemo? EnhancerByCGLIB? efe38465? FastClassByCGLIB? 38345933 Proxy class. These can be viewed in the generated proxy class.

Of course, it’s not clear what’s generated here, but you can see for yourself how bytecode is generated. Personally, I don’t understand.

Invoke is different from invokeSuper

The invoke method and the invokeSuper method are mentioned above. Let’s compare the invoke method and the invokeSuper method

public Object invoke(Object obj, Object[] args) throws Throwable {
  this.init();
  MethodProxy.FastClassInfo fci = this.fastClassInfo;
  return fci.f1.invoke(fci.i1, obj, args);
}
Copy the code

We can see that invoke uses the F1. invoke method, while invokeSuper uses the F2. invoke method.

First, take a look at the invoke method logic corresponding to F1

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

        try {
            switch(var10001) {
            case 0:
                var10000.welcome((String)var3[0]);
                return null; . }Copy the code

Call the Welcome method of the InfoDemo object directly.

This explains why we looped the Invoke method earlier, since the var2 we passed was a proxy object for InfoDemo, and as you can see from the first proxy class code, we went back to the Invoke method, causing an infinite loop.

Look again at the implementation of the corresponding Invoke in F2

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

        try {
            switch(var10001) {
             ....
                var10000.CGLIB$finalize$1(a);return null;
            case 16:
                var10000.CGLIB$welcome$0((String)var3[0]);
                return null; . }Copy the code

Because the var2 we pass in at this point is the InfoDemo proxy object, we end up calling the CGLIB$welcome$0 method in the proxy class.

summary

Cglib is much more complex than JDK dynamic proxy, mainly in the logic of generated code and generated code, which needs further study.

Furthermore, there are two invoke methods: invoke and invokeSuper, so use them with caution.

conclusion

This article is from personal notes, if there is any improper expression or mistake, welcome to correct.

With your mutual encouragement!!