Proxy and dynamic proxy pattern

Proxy pattern

Proxy pattern is a design pattern in which proxy acts as a substitution of the real object to be called by the client. It’s a wrapper of the original object that provides enhanced feature porting to same interface. See below graph.

public class ProxyTest {
  interface Run {
    void run();
  }
  static class RunImpl implements Run {
    public void run() {
      System.out.println("running");
    }
  }

  // RunProxy is a proxy of RunImpl.
  // It adds logs before and after the original run func.
  static class RunProxy implements Run {
    Run run;
    RunProxy(Run run) {
      this.run = run;
    }
    public void run() {
      System.out.println("before run");
      run.run();
      System.out.println("after run");
    }
  }

  public static void main(String ...args) {
    Run proxy = new RunProxy(new RunImpl());
    proxy.run();    
  }
  
}

Dynamic proxy

A proxy class is created for specific interface to enhance original class’s function. What if we want to add the log feature to other interfaces? Does that mean extra proxy class need to be created for every interface?

Dynamic proxy is the rescue. As the name suggests, it can create proxy class dynamically. We only need to write the enhanced function and rely on the proxy to create proxy class for us automatically. There are 2 common dynamic proxy approach. One uses java.lang.reflect.Proxy in jdk. Another leverages cglib. Show case reflect.Proxy below.

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

public class ProxyTest {
  interface Run {
    void run();
  }
  static class RunImpl implements Run {
    public void run() {
      System.out.println("running");
    }
  }

  // another interface
  interface Print {
    void print();
  }

  static class PrintImpl implements Print {
    public void print() {
      System.out.println("printing");
    }
  }

  static class MethodInterceptor implements InvocationHandler {
    Object obj;
    MethodInterceptor(Object obj) {
      this.obj = obj;
    }
    public Object invoke(Object proxy, Method method, Object []args) throws Throwable {
      System.out.println("dynamic proxy");
      System.out.println("before run");
      try {
        return method.invoke(obj, args);
      } catch(Exception e){
        return null;
      } finally {
        System.out.println("after run");
      }
    }
  }

  public static void main(String ...args) {
    // set flag to save auto generate class to disk
    System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    Run impl = new RunImpl();
    InvocationHandler ih = new MethodInterceptor(impl);
    Print print = new PrintImpl();
    InvocationHandler pih = new MethodInterceptor(print);
    try {
      // create Run proxy
      Run runProxy = (Run)Proxy.newProxyInstance(
          Run.class.getClassLoader(),
          new Class[]{Run.class},
          ih);
      runProxy.run();
      // create Print Proxy
      Print printProxy = (Print)Proxy.newProxyInstance(
          Print.class.getClassLoader(),
          new Class[]{Print.class},
          pih);
      printProxy.print();
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
}

Proxy.newProxyInstance will auto generate proxy class based on passed in interfaces and invocation handler. Below is one capture of the generated class decompiled by jd-gui.