为什么我可以在InvocationHandler的invoke()方法中调用proxy.getClass()?

时间:2016-03-19 03:24:14

标签: java reflection

这是来自" Thinking in Java"的修改过的动态代理示例。

> words[10000:10005,1]

在上面的示例中,我在import java.lang.reflect.*; interface Interface { void foo(); } class RealObject implements Interface { public void foo() {} } class DynamicProxyHandler implements InvocationHandler { private Object proxied; public DynamicProxyHandler(Object proxied) { this.proxied = proxied; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { proxy.toString(); return method.invoke(proxied, args); } } public class ProxyTest { public static void main(String args[]) { RealObject real = new RealObject(); Interface proxy = (Interface)Proxy.newProxyInstance( Interface.class.getClassLoader(), new Class[]{ Interface.class }, new DynamicProxyHandler(real)); proxy.foo(); } } 方法中调用toString()方法。正如我所料,无限递归将发生,因为调用代理的invoke()方法将再次调用Handler。

这就是布鲁斯·埃克尔(Bruce Eckel)在“思考Java”中所说的:

  

但是,在内部代理上调用方法时要小心   toString(),因为通过界面的调用被重定向   代理人。

异常详情:

invoke()

但是,如果我将Exception in thread "main" java.lang.StackOverflowError at DynamicProxyHandler.invoke(ProxyTest.java:19) at $Proxy0.toString(Unknown Source) at DynamicProxyHandler.invoke(ProxyTest.java:19) at $Proxy0.toString(Unknown Source) ... 替换为proxy.getClass();

proxy.toString();

一切都好。没有StackOverflowError。没有无限的递归。

我还尝试将public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { proxy.getClass(); return method.invoke(proxied, args); } 替换为proxy.toString();proxy.hashCode();。它们导致了StackOverflowError。

为什么proxy.equals("foo");getClass()toString()hashCode()不同?

1 个答案:

答案 0 :(得分:1)

答案可以在Proxy类的文档中找到:https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html

  

在代理实例上的java.lang.Object中声明的hashCode,equals或toString方法的调用将被编码并调度到调用处理程序的invoke方法,其方式与接口方法调用的编码和分派方式相同,如如上所述。传递给invoke的Method对象的声明类将是java.lang.Object。从java.lang.Object继承的代理实例的其他公共方法不会被代理类覆盖,因此这些方法的调用就像它们对java.lang.Object的实例一样。

另外,我认为这与toString()方法有默认定义这一事实有关:

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

虽然getClass()(和wait()notify()等)定义为:

public final native Class<?> getClass();

因此,要区分哪些方法不会被代理,您可以在方法定义中看到final native的存在。

相关问题