分析环境: jdk8

dynamic proxy 是啥?

A dynamic proxy class is a class that implements a list of interfaces1 specified at runtime such that a method invocation through one of the interfaces on an instance of the class will be encoded and dispatched to another object through a uniform interface2.

  1. 动态代理类生成调用方法如下:
Proxy.newProxyInstance(handler.getClass().getClassLoader(), new Class[]{Dynasty.class},handler);
  • handler.getClass().getClassLoader() 运行时动态生成的代理类 load 到 jvm 使用的 class loader。
  • new Class[]{Dynasty.class} 运行时动态生成的代理类实现的一系列接口。
  • handler Invocation Handler 通过 invoke() 来分发方法,包含被实际代理的对象实例。

保存运行时生成的动态代理类

ProxyGenerator.java

    /** debugging flag for saving generated class files */
    private final static boolean saveGeneratedFiles =
        java.security.AccessController.doPrivileged(
            new GetBooleanAction(
                "sun.misc.ProxyGenerator.saveGeneratedFiles")).booleanValue();

设置 sun.misc.ProxyGenerator.saveGeneratedFiles

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

运行时生成的动态代理类($Proxy0)分析

运行时生成的动态代理类实例化

dynamic-proxy-instance

$Proxy0 -> Proxy(InvocationHandler h) 由此可见:

  1. 动态代理类 $Proxy0 实例化时,调用 super class Proxy 的构造函数,需要的 InvocationHandler 是实现了该接口的 DynastyDynamicProxy
  2. DynastyDynamicProxy 实例化时,构造函数需要的是 TangDynasty 这个实际被代理的类。

dynamic-proxy-class-method-founder

运行时生成的动态代理类方法

$Proxy0 的命名

        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

$Proxy0#founder

dynamic-proxy-stack-frame-1


    public final String founder() throws  {
        try {
            return (String)super.h.invoke(this, m4, (Object[])null); // super.h.invoke 指的就是 DynastyDynamicProxy.invoke
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

$Proxy0#reviver

    public final String reviver() throws  {
        try {
            return (String)super.h.invoke(this, m5, (Object[])null); // super.h.invoke 指的就是 DynastyDynamicProxy.invoke
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

$Proxy0#lastEmperor

    public final String lastEmperor() throws  {
        try {
            return (String)super.h.invoke(this, m3, (Object[])null); // super.h.invoke 指的就是 DynastyDynamicProxy.invoke
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

由上可知,$Proxy0 实现的方法的统一调用入口(uniform interface) 就是 DynastyDynamicProxy#invoke,也就是 jdk 动态代理必须实现的 InvocationHandler#invoke 方法。

InvocationHandler 接口实现类 DynastyDynamicProxy 分析


public class DynastyDynamicProxy implements InvocationHandler {

    // 被代理的对象
    private Object delegate;
    public DynastyDynamicProxy(Object originalDynamic){
        this.delegate = originalDynamic;
    }
    /**
    @param proxy 当前动态代理类 $Proxy0 的实例
    @param method 当前动态代理类调用的方法
    @param args 当前动态代理类调用的方法的参数
    **/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("公元 618 年唐朝建立!");
        String res = (String) method.invoke(delegate,args);
        System.out.println(method.getName() + " of Tang Dynasty was " + res + "!");
        System.out.println("公元 907 年唐朝灭亡!\n");
        return res;
    }
}

dynamic-proxy-method-call

被代理的类 TangDynasty 分析

public class TangDynasty implements Dynasty{

    @Override
    public String founder() {
        return "李渊";
    }

    @Override
    public String reviver() {
        return "李隆基";
    }

    @Override
    public String lastEmperor() {
        return "李柷";
    }
}
  • 调用栈可知,最终还是要调用被代理的类的方法。
  • 但是可以在执行真正的代理类方法之前,在 InvocationHandler#invoke 中添加实际需要的逻辑。这就是中间层,通过中间层进行逻辑的增强。
  • 这样看起来就好像对真正的代理对象进行了增强。

References

  1. dynamic-proxy-class-method
  2. jdk-com-sun-proxy
  3. ProxyGenerator#sun.misc.ProxyGenerator.saveGeneratedFiles
  4. jdk8-proxy
  5. 分析所用代码
  6. $Proxy0
  7. jdk-porxy-and-cglib