`
dsxwjhf
  • 浏览: 70521 次
  • 性别: Icon_minigender_1
  • 来自: 安徽
社区版块
存档分类
最新评论

动态代理是如何工作的?

阅读更多
我们知道,动态代理主要基于 java.lang.reflect 包下的两个类,一个是 InvocationHandler ,一个是 Proxy 。一个简单的 Dynamic Proxy 的构造过程如下:

==================================================

public class MyInvocationHandler implements InvocationHandler {

    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // Do something here.
        return method.invoke(target, args);
        // Do something here.
    }
}

public class ProxyGenerator {

    public static Object generateProxy(Object target, InvocationHandler h) {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), h);
    }

}

Hello hello = new Hello();
MyInvocationHandler h = new MyInvocationHandler();
h.setTarget(hello);
Object proxy = ProxyGenerator.generateProxy(hello, h);
IHello iHello = (IHello) proxy;
iHello.sayHello();

==================================================

OK ,这段大家都知道,但 invoke() 方法到底是如何作用到 Proxy 上的呢?
我们可以加上 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
这句话,让生成的 Proxy 字节码存储在硬盘上,然后反编译该字节码,看看动态代理的原理。

观察生成的代理类的源码,我们发现它覆写了接口中的所有方法,以及从 java.lang.Object 中继承过来的方法,如: toString() 、 equals() 、 hashCode() 等。以 sayHello() 方法为例,在代理类中它被覆写成了这个样子:
public final void sayHello() {
    // 此处省略虚拟机自动添加的异常处理等
    this.h.invoke(this, m3, null);
    return;
}

这些方法使用这种统一的格式进行方法调用:
this.h.invoke(this, method, args);
下面来解释一下这句话的各个部分。

this.h: InvocationHandler 的实例。我们知道, Proxy.newProxyInstance() 的时候,把一个 InvocationHandler 实例作为它的第三个参数。事实上在最终生成的 Proxy 中,它持有这个 InvocationHandler 实例,所以能通过 this 引用到。

invoke: 没有任何高深的东西,它就是一个 InvocationHandler 的实例 this.h 对它的实例方法 invoke() 的一次普通调用。换句话说,我们在 InvocationHandler 中覆写的方法在这里被回调了,发挥作用了!我们再来复习一下这个方法的定义:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
第一个参数代表 proxy ,在最终生成的 Proxy 实现中就是它自己。
第二个参数代表方法,比如接口中的方法、 toString 、 equals 、 hashCode 等。这些方法将在代理类的静态块中初始化好,比如:
static {
    m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[1] { Class.forName("java.lang.Object") });
    m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
    m3 = Class.forName("com.test.IHello").getMethod("sayHello", new Class[0]);
}
第三个参数不用解释,代表参数。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics