简单回顾一下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版本和这些包版本的变化迭代可能会有差异。最靠谱的还是根据各自使用的环境版本自行测试验证。