Java Dynamic Proxy and CGLIB are described in detail

  • 2020-06-12 09:00:39
  • OfStack

Static proxy mode

Because some functions need to be processed twice, or some functions are not known to the outside world, you can use the proxy mode, by accessing the third party, indirect access to the original function, to achieve the above purpose.


interface Hosee{
  String sayhi();
}

class Hoseeimpl implements Hosee{

  @Override
  public String sayhi()
  {
    return "Welcome oschina hosee's blog";
  }

}

class HoseeProxy implements Hosee{

  Hosee h;

  public HoseeProxy(Hosee h)
  {
    this.h = h;
  }

  @Override
  public String sayhi()
  {
    System.out.println("I'm proxy!");
    return h.sayhi();
  }

}


public class StaticProxy
{

  public static void main(String[] args)
  {
    Hoseeimpl h = new Hoseeimpl();
    HoseeProxy hp = new HoseeProxy(h);
    System.out.println(hp.sayhi());
  }

}

1.1 Disadvantages of static proxy

If you want to broker for more than one class, you need to create more than one proxy class, making maintenance more difficult.

When you think about it, the reason why static agents have these problems is because agents are determined at compile time, and these problems are easier to solve if the agent occurs at run time, so the presence of dynamic agents is necessary.

2. Dynamic proxy


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface HoseeDynamic
{
  String sayhi();
}

class HoseeDynamicimpl implements HoseeDynamic
{
  @Override
  public String sayhi()
  {
    return "Welcome oschina hosee's blog";
  }
}

class MyProxy implements InvocationHandler
{
  Object obj;
  public Object bind(Object obj)
  {
    this.obj = obj;
    return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
        .getClass().getInterfaces(), this);
  }
  @Override
  public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable
  {
    System.out.println("I'm proxy!");
    Object res = method.invoke(obj, args);
    return res;
  }
}

public class DynamicProxy
{
  public static void main(String[] args)
  {
    MyProxy myproxy = new MyProxy();
    HoseeDynamicimpl dynamicimpl = new HoseeDynamicimpl();
    HoseeDynamic proxy = (HoseeDynamic)myproxy.bind(dynamicimpl);
    System.out.println(proxy.sayhi());
  }
}

By analogy with static proxies, you can see that instead of implementing the original interface, the proxy class implements InvocationHandler. through


Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
        .getClass().getInterfaces(), this);

To dynamically generate a proxy class with the same classloader as the proxy class and the same interface as the proxy class.

The proxy class generated by the above method is equivalent to the proxy class in the static proxy.

This makes it possible to decide what the proxy object should be at run time and solves the problem of static proxies.

When a dynamically generated proxy class calls a method, the invoke method is triggered, and the methods of the proxy-class can be enhanced in the invoke method.

The benefits can be clearly seen with dynamic proxies. When using static proxies, if some classes of different interfaces want to use the proxy pattern to implement the same functionality, multiple proxy classes will be implemented, but in dynamic proxies only one proxy class is needed.

In addition to saving the workload of writing proxy classes, dynamic proxy realizes that the proxy behavior of proxy classes can be determined when the original class and interface are unknown. When the proxy class is separated from the original class, it can be used flexibly in different application scenarios.

2.1 Disadvantages of dynamic proxy

Both the proxy and delegate classes need to implement the same interface. That is, only classes that implement an interface can use the Java dynamic proxy mechanism. However, the reality is that not every class you encounter in use will give you an interface. Therefore, this mechanism cannot be used for classes that do not implement interfaces.

CGLIB, on the other hand, can implement dynamic proxies to classes

2.2 Principle of callback function

As mentioned above, the invoke method is triggered when a dynamically generated proxy class calls a method.

Obviously the invoke method is not called to display, it is a callback function, so how is the callback function called?

In the code for the dynamic agent described above, only 1 ambiguity exists


Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
        .getClass().getInterfaces(), this);

Tracking the source code of this method, you can see that the program has performed validation, optimization, caching, synchronization, generating bytecode, displaying class loading, and so on. The previous steps are not our focus, and finally it called


byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
        proxyName, interfaces);

This method is used to complete the bytecode generation action, which at runtime produces a bytecode byte[] array describing the proxy class.

Add to the main function


System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

After adding this code and running the program again, a proxy class Class file named "$Proxy().class" will be generated on disk. After decomp (I used JD-ES71en for decomp), you can see the following code:


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy
 implements HoseeDynamic
{
 private static Method m1;
 private static Method m3;
 private static Method m0;
 private static Method m2;

 public $Proxy0(InvocationHandler paramInvocationHandler)
  throws 
 {
  super(paramInvocationHandler);
 }

 public final boolean equals(Object paramObject)
  throws 
 {
  try
  {
   return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
  }
  catch (Error|RuntimeException localError)
  {
   throw localError;
  }
  catch (Throwable localThrowable)
  {
   throw new UndeclaredThrowableException(localThrowable);
  }
 }

 public final String sayhi()
  throws 
 {
  try
  {
   return (String)this.h.invoke(this, m3, null);
  }
  catch (Error|RuntimeException localError)
  {
   throw localError;
  }
  catch (Throwable localThrowable)
  {
   throw new UndeclaredThrowableException(localThrowable);
  }
 }

 public final int hashCode()
  throws 
 {
  try
  {
   return ((Integer)this.h.invoke(this, m0, null)).intValue();
  }
  catch (Error|RuntimeException localError)
  {
   throw localError;
  }
  catch (Throwable localThrowable)
  {
   throw new UndeclaredThrowableException(localThrowable);
  }
 }

 public final String toString()
  throws 
 {
  try
  {
   return (String)this.h.invoke(this, m2, null);
  }
  catch (Error|RuntimeException localError)
  {
   throw localError;
  }
  catch (Throwable localThrowable)
  {
   throw new UndeclaredThrowableException(localThrowable);
  }
 }

 static
 {
  try
  {
   m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
   m3 = Class.forName("HoseeDynamic").getMethod("sayhi", new Class[0]);
   m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
   m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
   return;
  }
  catch (NoSuchMethodException localNoSuchMethodException)
  {
   throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  }
  catch (ClassNotFoundException localClassNotFoundException)
  {
   throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  }
 }
}

The dynamic proxy class proxy not only the methods in the interface that are displayed, but also the three methods inherited from Object's root class, equals(), hashcode(), and toString(), and nothing more.

As you can see from the above code, no matter which method is called, the invoke method of InvocationHandler is called, with different arguments.

2.3 Differences between dynamic agents and static agents

The code of the Proxy class is fixed and will not grow larger as the business grows larger;

AOP programming can be implemented, which is not possible with static proxies;

Decoupling, if used in web services, can achieve the separation of the data layer and the business layer.

The advantage of dynamic proxies is to implement non-intrusive code extensions. The problem with the static proxy pattern itself is that as the number of methods grows, the code volume of the proxy class becomes 10 percent larger. So dynamic proxy is introduced to solve this kind of problem

3. CGLIB

cglib implements a proxy for a class. Its principle is to generate a subclass of the specified target class and override the methods to implement enhancements, but because of inheritance, final decorated classes cannot be proxy.


import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

class CGlibHosee
{
  public String sayhi()
  {
    return "Welcome oschina hosee's blog";
  }
}

class CGlibHoseeProxy
{
  Object obj;

  public Object bind(final Object target)
  {
    this.obj = target;
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(obj.getClass());
    enhancer.setCallback(new MethodInterceptor()
    {
      @Override
      public Object intercept(Object obj, Method method, Object[] args,
          MethodProxy proxy) throws Throwable
      {
        System.out.println("I'm proxy!");
        Object res = method.invoke(target, args);
        return res;
      }
    });
    return enhancer.create();
  }

}

public class CGlibProxy
{
  public static void main(String[] args)
  {
    CGlibHosee cGlibHosee = new CGlibHosee();
    CGlibHoseeProxy cGlibHoseeProxy = new CGlibHoseeProxy();
    CGlibHosee proxy = (CGlibHosee) cGlibHoseeProxy.bind(cGlibHosee);
    System.out.println(proxy.sayhi());
  }
}

cglib needs to specify the parent class and callback methods. Of course, cglib can also be interface oriented like Java dynamic proxy 1, because the essence is inheritance.

Thank you for reading, I hope to help you, thank you for your support to this site!


Related articles: