前言:动态代理中所谓的"动态",是针对使用java代码实际编写了代理类的"静态"代理而言的,它的优势不在于省去了编写代理类的那点工作量,而是实现了可以在原始类和接口还未知的时候,就确定了代理行为。
上示例:
public class ClassLoadLearn {
static interface Parent{
void sayHello();
}
static class Son implements Parent {
@Override
public void sayHello() {
System.out.println("hello");
}
}
static class JdkProxy implements InvocationHandler{
Object targetObj;
Object bind(Object targetObj){
this.targetObj = targetObj;
return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(),targetObj.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
System.out.println(proxy.getClass());
return method.invoke(targetObj, args);
}
}
public static void main(String[] args) {
/*加入这个设置,磁盘中将会产生一个"$Proxy0.class"代理类文件*/
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Parent son = (Parent) new JdkProxy().bind(new Son());
son.sayHello();
}
}
Proxy.newProxyInstance()跟踪这个方法的源码,可以看到程序进行了验证、优化、缓存、同步、生成字节码和显示类加载等操作,该方法最后调用了sun.misc.ProxyGenerator.generateProxyClass()方法来完成生成字节码动作,这个方法可以在运行是产生一个 描述代理类的字节码byte[]数组,并加载该代理类
反编译生成的$Proxy0.calss文件:
图一:
图二:
图三:
从上述三张截图中代码可以看出,JdkProxy类执行bind()方法返回的是由sun.misc.ProxyGenerator.generateProxyClass()方法先生成的字节码byte[]数组,再由类加载器动态加载生成的代理类
代理类执行sayHello()方法时是调用的InvocationHandler的实现类JdkProxy,从下面的代码看出获取的InvocationHandler实例为当前对象:
Proxy.newProxyInstance(targetObj.getClass().getClassLoader(),targetObj.getClass().getInterfaces(),this);
所以最终是执行的是JdkProxy的invoke()方法,从图二和图三可以看出invoke()方法的Method入参是是通过反射获取的Parent接口的sayHello()方法,执行Method的invoke()方法的第一个入参实例是我们的代理对象Son。
Parent son = (Parent) new JdkProxy().bind(new Son());
最终执行输出为:
下面贴出$Proxy.class使用javap反编译的输出内容以供参考: