简单回顾一下JDK内部动态代理机制。

基本用法

Java原生提供了动态代理机制,通过内部机制可以实现一定层面的AOP处理逻辑。Java动态代理只能处理接口方法,不能直接在类级别上进行操作。直接看一个最简单的例子,

public interface Foobar {
    String hello();
}

public class ProxyHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        return "proxy";
    }
}

Foobar foobar = (Foobar) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] {Foobar.class}, new ProxyHandler());
System.out.println(foobar.hello());

动态代理类需要实现InvocationHandler,在invoke方法中可以获取到外部调用的方法,在invoke方法中可以再根据具体的待调用方法与参数进行拦截处理。

内部实现

深入到Proxy.newProxyInstance内部去观察一下内部的代码实现,可以找到几个关键函数调用,

  • Proxy.getProxyClass0
  • ProxyClassFactory.apply
  • ProxyGenerator.generateProxyClass

动态代理类的创建在ProxyClassFactory.apply中,流程代码不多,容易看到最终生成的类名是"com.sun.proxy.$Proxy{num}"形式。实际生成的Class中包含了怎样的内容,可以在generateProxyClass中去看,不过这个就很细了,要结合Class文件格式定义去看。

另外通过设置"sun.misc.ProxyGenerator.saveGeneratedFiles"为true,可以将Proxy的Class文件保存到文件系统,后续再使用javap去观察具体的字节码指令。

方案对比

实际项目中可以考虑的方案除了原生动态代理之外,另外的选择有,

  • cglib
  • javaassit

两者都可以进行字节码修改,另外javaassit也提供了动态代理。直接基于字节码进行处理,在能力上会更丰富,能够处理非接口方法。在此之外的差别就在于性能了。

关于几种方案的性能,随着Java版本和这些包版本的变化迭代可能会有差异。最靠谱的还是根据各自使用的环境版本自行测试验证。