这是来自" 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()
不同?
答案 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
的存在。