ygnzyh
2013-06-21 17:20
浏览 428
已采纳

Java中代理(Proxy)的实现机制

看TIJ的代理看得有点晕,其中有一段代码如下。(print是包装了下的System.out.println)

[code="java"]
package TypeInfo;
// typeinfo/SimpleDynamicProxy23.java
// TIJ4 Chapter Typeinfo, Exercise 23, page 598
// Inside invoke() in SimpleDynamicProxy.java, try to print the proxy argument and explain
// what happens.
import java.lang.reflect.*;

interface Interface {
void doSomething();
void somethingElse(String arg);
}

class RealObject implements Interface {
public void doSomething() { print("doSomething"); }
public void somethingElse(String arg) {
print("somethingElse " + arg);
}
}

class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
* trying to print proxy leads to:
* StackOverFlowError
* at AbstractStringBuilder.(Unknown Source)
* at StringBuilder.(Unknown Source)
* at DynamicProxyHandler.invoke(SimpleDynamicProxy23.java)
* at $Proxy0.toString(Unknown Source)
* at String.valueOf(Unknown Source)
* at StrinbBuilcer.append(Unknown Source)
* at DynamicProxyHandler.invoke(SimpleDynamicProxy23.java), etc,
* probably due to infinite recursion because calls to toString()
* are passed repeatedly back to this invoke method
/
// System.out.println("proxy: " + proxy); // error
System.out.println("
*** proxy: " + proxy.getClass() +
", method: " + method + ", args: " + args);
if(args != null)
for(Object arg : args)
System.out.println(" " + args);
return method.invoke(proxied, args);
}
}

class SimpleDynamicProxy23 {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
RealObject real = new RealObject();
consumer(real);
// Insert a proxy and call again:
Interface proxy = (Interface)Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[]{ Interface.class },
new DynamicProxyHandler(real));
consumer(proxy);

}
}
[/code]
代码比较容易看懂,但是其中的内在机制就很隐晦。调试时发现,consumer中调用Interface的方法后程序流直接就跳入了invoke。这是由什么机制决定的?换言之,在consumer中对接口Interface的方法的调用是如何转交给DynamicProxyHandler的invoke方法的?
书中有一句话:“在invoke()内部,在代理上调用方法时需要格外当心,因为对接口的调用将被重定向为对代理的调用。”这句解释了注释掉的“// System.out.println("proxy: " + proxy); // error”的错误是因为引起了无限递归(很空洞的解释,具体怎样引起?)invoke的第一个参数proxy是什么类型?
希望大家不吝赐教。3Q!

  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

4条回答 默认 最新

  • jinnianshilongnian 2013-06-21 19:02
    已采纳

    推荐你看下
    [url]http://hllvm.group.iteye.com/group/topic/36843[/url]

    然后dump出代理类出来 然后观察

    我这里给你一份我dump出的
    [code="java"]
    package TypeInfo;

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;

    public final class $Proxy0 extends Proxy
    implements Interface
    {
    private static Method m1;
    private static Method m0;
    private static Method m3;
    private static Method m4;
    private static Method m2;

    public final void doSomething()
    {
    try
    {
    this.h.invoke(this, m3, null);
    return;
    }
    catch (RuntimeException localRuntimeException)
    {
    throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    throw new UndeclaredThrowableException(localThrowable);
    }
    }

    public final void somethingElse(String paramString)
    {
    try
    {
    this.h.invoke(this, m4, new Object[] { paramString });
    return;
    }
    catch (RuntimeException localRuntimeException)
    {
    throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    throw new UndeclaredThrowableException(localThrowable);
    }
    }

    public $Proxy0(InvocationHandler paramInvocationHandler)
    {
    super(paramInvocationHandler);
    }

    public final int hashCode()
    {
    try
    {
    return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (RuntimeException localRuntimeException)
    {
    throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    throw new UndeclaredThrowableException(localThrowable);
    }
    }

    public final boolean equals(Object paramObject)
    {
    try
    {
    return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (RuntimeException localRuntimeException)
    {
    throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    throw new UndeclaredThrowableException(localThrowable);
    }
    }

    public final String toString()
    {
    try
    {
    return ((String)this.h.invoke(this, m2, null));
    }
    catch (RuntimeException localRuntimeException)
    {
    throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    throw new UndeclaredThrowableException(localThrowable);
    }
    }

    static
    {
    try
    {
    m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
    m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
    m3 = Class.forName("TypeInfo.Interface").getMethod("doSomething", new Class[0]);
    m4 = Class.forName("TypeInfo.Interface").getMethod("somethingElse", new Class[] { Class.forName("java.lang.String") });
    m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
    return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
    throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
    throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
    }
    }[/code]

    打赏 评论
  • QiangGe2Dai 2013-06-21 17:56

    坐等大牛解释,不过invoke的第一个参数proxy应该是被代理的一个实例,就是目标代理类,可以是一个接口的实现或者是一个类的子类...继续

    打赏 评论
  • iteye_15645 2013-06-22 09:25

    调试时发现,consumer中调用Interface的方法后程序流直接就跳入了invoke。这是由什么机制决定的?
    ——————
    Interface proxy = (Interface)Proxy.newProxyInstance(

    Interface.class.getClassLoader(),

    new Class[]{ Interface.class },

    new DynamicProxyHandler(real));

    consumer(proxy);
    这些代码决定的。
    Proxy.newProxyInstance代码是JDK的代码,楼主是想了解JDK的底层实现...?
    要了解这个, 就要了解JDK类字节码的生成, 了解了类字节码的生成就要了解类的加载器...

    真没必要去纠结JDK的底层实现...如果想了解, 建议去看看《JAVA虚拟机》

    打赏 评论
  • baochensu 2013-06-22 14:44

    我写过一个博客文章:[url]http://dz.sdut.edu.cn/blog/subaochen/?p=232[/url] 简单解释了java动态代理的实现机制,希望能够帮到你。

    打赏 评论

相关推荐 更多相似问题