The difference between Jdk dynamic proxy and Cglib dynamic proxy

One: Foreword:

The proxy mode is divided into static proxy and dynamic proxy. Today I will talk about two common and widely used implementation methods of dynamic proxy—-jdk dynamic proxy and Cglib dynamic proxy

Two: Jdk dynamic proxy implementation analysis:

The structural diagram is as follows. I defined a UserService interface with a getUserName method. Then an implementation class UserServiceimpl is defined to implement this interface.

JDK dynamic proxy steps

1. Create a class that implements the interface InvocationHandler, which must implement the invoke method
2. Create the proxied class and interface
3. Create a proxy through Proxy’s static method newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)
4. Call the method through the proxy

Here is the code directly.

Interface object of UserService proxy

public interface UserService {<!-- -->
    
    public void getUserName();

}

UserServiceimpl The implementation class of the proxy

public class UserServiceimpl implements UserService{<!-- -->
    @Override
    public void getUserName() {<!-- -->
        System.out.println("Old Mo, Brother Qiang said he wants to eat sharks");
    }
}

JDKProxy

public class JDKProxy implements InvocationHandler {<!-- -->

    Object target;

    public JDKProxy(Object target) {<!-- -->
        this. target = target;
    }

     /**
     * This method is responsible for centralized processing of all method calls on the dynamic proxy class.
     * The call processor is preprocessed or dispatched to the delegate class instance for reflection execution according to these three parameters
     *
     * @param proxy proxy class instance
     * @param method The method object to be called
     * @param args call parameters
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {<!-- -->
        // pre-enhanced method
        System.out.println("Tell Lao Mo that I want to eat fish");
        // execute method
        Object invoke = method. invoke(target, args);
        // post-enhancement method
        System.out.println("Okay, I will send it to Brother Qiang");
        return invoke;
    }

}

main method

    public static void main(String[] args) throws Exception{<!-- -->
        // create entity class object
        UserService userService=new UserServiceimpl();

        // Create an InvocationHandler associated with the proxy object
        InvocationHandler UserHandler = new JDKProxy(userService);

        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");

        //Create a proxy object stuProxy to proxy UserHandler, each execution method of the proxy object will replace the invoke method in Invocation
        UserService jdkUserService= (UserService)Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class<?>[]{<!-- -->UserService.class},UserHandler);

        // proxy execution method
        jdkUserService. getUserName();

    }
\tPrint:
Tell Lao Mo I want to eat fish
Old Mo, brother Qiang said he wants to eat sharks
Okay, I'll send it to Brother Qiang later.
\t\t\t
Process ended with exit code 0

It can be seen that this line of code is the key code of our jdk dynamic agent, so what does this line of code do?

UserService jdkUserService= (UserService)Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class<?>[]{<!-- -->UserService.class},UserHandler);

We pass System.getProperties().put(“jdk.proxy.ProxyGenerator.saveGeneratedFiles”, “true”); This line of code will produce a binary file of a temporary object, which will be decompiled into a java file.

Proxy0 proxy object

/**
*
*/
public final class $Proxy0 extends Proxy implements UserService {<!-- -->
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
\t
/**
* First of all, we see that it has a parameterized construction method, InvocationHandler var1. When producing a proxy object, an InvocationHandler object is passed through the construction method.
*/
    public $Proxy0(InvocationHandler var1) throws {<!-- -->
        super(var1);
    }

    public final boolean equals(Object var1) throws {<!-- -->
        try {<!-- -->
            return (Boolean) super.h.invoke(this, m1, new Object[]{<!-- -->var1});
        } catch (RuntimeException | Error var3) {<!-- -->
            throw var3;
        } catch (Throwable var4) {<!-- -->
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws {<!-- -->
        try {<!-- -->
            return (String) super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {<!-- -->
            throw var2;
        } catch (Throwable var3) {<!-- -->
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void getUserName() throws {<!-- -->
        try {<!-- -->
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {<!-- -->
            throw var2;
        } catch (Throwable var3) {<!-- -->
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws {<!-- -->
        try {<!-- -->
            return (Integer) super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {<!-- -->
            throw var2;
        } catch (Throwable var3) {<!-- -->
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {<!-- -->
        try {<!-- -->
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("byprocuration.spring.service.UserService").getMethod("getUserName");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {<!-- -->
            throw new NoSuchMethodError(var2. getMessage());
        } catch (ClassNotFoundException var3) {<!-- -->
            throw new NoClassDefFoundError(var3. getMessage());
        }
    }

1. Proxy0 implements the UserService interface and inherits the Proxy class
2. There is a construction method, and an InvocationHandler object is passed through the construction method, and this object is our JDKProxy that implements the InvocationHandler interface. And assign a value to the h attribute of the parent class Proxy.

 protected InvocationHandler h;

    protected Proxy(InvocationHandler h) {<!-- -->
        Objects. requireNonNull(h);
        this.h = h;
    }
  1. There are a lot of variables m0, m1, m2, m3, and they correspond to the method names.
  2. When we call the getUserName() method, he will call super.h.invoke(this, m3, (Object[])null), which is actually calling the invoke method of the JDKProxy we defined.

Summarize:

  1. When we use jdk dynamic proxy, jdk generates $Proxy0 proxy class for us. This class file is placed in memory. When we create a proxy object, we obtain the construction method of this class through reflection, and then create a proxy instance .
  2. When we call the getUserName method, he will first call the invoke method of the parent class attribute h. When instantiated, the JDKProxy we defined will be assigned to h.
  3. We will find that $Proxy0 needs to implement the UserService interface, which is equivalent to the JDK dynamic proxy based on the interface.

3. Analysis of Cglib dynamic proxy implementation:

To use the Cglib proxy, you need to import the asm version package first, and implement the MethodInterceptor interface

cglibProxy

public class cglibProxy implements MethodInterceptor {<!-- -->

    private Enhancer enhancer = new Enhancer();

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {<!-- -->
        System.out.println("Tell Lao Mo that I want to eat fish");
        Object result = methodProxy.invokeSuper(o,objects);
        System.out.println("Okay, I will send it to Brother Qiang");
        return result;

    }
    
    public Object getProxy(Class clazz){<!-- -->
        //Set the class to create a subclass, that is, specify which class to generate a proxy class for
        enhancer. setSuperclass(clazz);

        /*Set callback function
         * setCallback sets the processing class when the public non-final method of the proxy class is called
         * */
        enhancer. setCallback(this);
        //Create subclass instance
        return enhancer. create();
    }
}

Execute the main method call

 public static void main(String[] args) {<!-- -->

        // Instantiate the CglibProxy object
        cglibProxy cglibProxy=new cglibProxy();
        // //Get the proxy object
        UserServiceimpl cglibProxyUserService =(UserServiceimpl)cglibProxy.getProxy(UserServiceimpl.class);
        cglibProxyUserService.getUserName();
    }
\t\t\t
\t\t\tPrint:
Tell Lao Mo I want to eat fish
Old Mo, brother Qiang said he wants to eat sharks
Okay, I'll send it to Brother Qiang later.

Cglib creates proxy class objects through Enhancer. And a very low-level bytecode technology (ASM) is adopted. The principle is to create a subclass for a class through bytecode technology, and use method interception technology in the subclass to intercept all parent class method calls, and weave it into Cross cutting logic.

Enhancer is a frequently used class in cglib. It is a bytecode enhancer that can be used to create proxies for classes without interfaces. Its function is quite similar to the Proxy class that comes with java. It creates subclasses from a given class, and all non-final methods have callback hooks.

You can check out this article to find out

Summary:

Finally, let’s summarize the difference between JDK dynamic proxy and Gglib dynamic proxy:

1. JDK dynamic proxy implements the interface of the proxy object, and Cglib inherits the proxy object.

2. Both JDK and Cglib generate bytecodes at runtime. JDK directly writes Class bytecodes, and Cglib uses the ASM framework to write Class bytecodes. Cglib proxy implementation is more complicated, and the generation of proxy classes is less efficient than JDK.

3. JDK calls the proxy method through the reflection mechanism, and Cglib directly calls the method through the FastClass mechanism. Cglib has higher execution efficiency

.