天天看點

InvocationHandler中invoke()方法的調用問題

Java中動态代理的實作,關鍵就是這兩個東西:Proxy、InvocationHandler,下面從InvocationHandler接口中的invoke方法入手,簡單說明一下Java如何實作動态代理的。 

        首先,invoke方法的完整形式如下: 

Java代碼  

  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  
  2.     {  
  3.         method.invoke(obj, args);  
  4.         return null;  
  5.     }  

        首先猜測一下,method是調用的方法,即需要執行的方法;args是方法的參數;proxy,這個參數是什麼?以上invoke()方法的實作即是比較标準的形式,我們看到,這裡并沒有用到proxy參數。檢視JDK文檔中Proxy的說明,如下: 

Java代碼  

  1. A method invocation on a proxy instance through one of its proxy interfaces will be dispatched to the invoke method of the instance's invocation handler, passing the proxy instance,a java.lang.reflect.Method object identifying the method that was invoked, and an array of type Object containing the arguments.  

        由此可以知道以上的猜測是正确的,同時也知道,proxy參數傳遞的即是代理類的執行個體。 

        為了友善說明,這裡寫一個簡單的例子來實作動态代理。 

        Java代碼  

  1. //抽象角色(動态代理隻能代理接口)  
  2. public interface Subject {  
  3.     public void request();  
  4. }  

Java代碼  

  1. //真實角色:實作了Subject的request()方法  
  2. public class RealSubject implements Subject{  
  3.     public void request(){  
  4.         System.out.println("From real subject.");  
  5.     }  
  6. }  

Java代碼  

  1. //實作了InvocationHandler  
  2. public class DynamicSubject implements InvocationHandler  
  3. {  
  4.     private Object obj;//這是動态代理的好處,被封裝的對象是Object類型,接受任意類型的對象  
  5.     public DynamicSubject()  
  6.     {  
  7.     }  
  8.     public DynamicSubject(Object obj)  
  9.     {  
  10.         this.obj = obj;  
  11.     }  
  12.     //這個方法不是我們顯示的去調用  
  13.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  
  14.     {  
  15.         System.out.println("before calling " + method);  
  16.         method.invoke(obj, args);  
  17.         System.out.println("after calling " + method);  
  18.         return null;  
  19.     }  
  20. }  

Java代碼  

  1. //用戶端:生成代理執行個體,并調用了request()方法  
  2. public class Client {  
  3.     public static void main(String[] args) throws Throwable{  
  4.         // TODO Auto-generated method stub  
  5.         Subject rs=new RealSubject();//這裡指定被代理類  
  6.         InvocationHandler ds=new DynamicSubject(rs);  
  7.         Class<?> cls=rs.getClass();  
  8.         //以下是一次性生成代理  
  9.         Subject subject=(Subject) Proxy.newProxyInstance(  
  10.                 cls.getClassLoader(),cls.getInterfaces(), ds);  
  11.         //這裡可以通過運作結果證明subject是Proxy的一個執行個體,這個執行個體實作了Subject接口  
  12.         System.out.println(subject instanceof Proxy);  
  13.         //這裡可以看出subject的Class類是$Proxy0,這個$Proxy0類繼承了Proxy,實作了Subject接口  
  14.         System.out.println("subject的Class類是:"+subject.getClass().toString());  
  15.         System.out.print("subject中的屬性有:");  
  16.         Field[] field=subject.getClass().getDeclaredFields();  
  17.         for(Field f:field){  
  18.             System.out.print(f.getName()+", ");  
  19.         }  
  20.         System.out.print("\n"+"subject中的方法有:");  
  21.         Method[] method=subject.getClass().getDeclaredMethods();  
  22.         for(Method m:method){  
  23.             System.out.print(m.getName()+", ");  
  24.         }  
  25.         System.out.println("\n"+"subject的父類是:"+subject.getClass().getSuperclass());  
  26.         System.out.print("\n"+"subject實作的接口是:");  
  27.         Class<?>[] interfaces=subject.getClass().getInterfaces();  
  28.         for(Class<?> i:interfaces){  
  29.             System.out.print(i.getName()+", ");  
  30.         }  
  31.         System.out.println("\n\n"+"運作結果為:");  
  32.         subject.request();  
  33.     }  
  34. }  

Xml代碼  

  1. 運作結果如下:此處省略了包名,***代替  
  2. true  
  3. subject的Class類是:class $Proxy0  
  4. subject中的屬性有:m1, m3, m0, m2,   
  5. subject中的方法有:request, hashCode, equals, toString,   
  6. subject的父類是:class java.lang.reflect.Proxy  
  7. subject實作的接口是:cn.edu.ustc.dynamicproxy.Subject,   
  8. 運作結果為:  
  9. before calling public abstract void ***.Subject.request()  
  10. From real subject.  
  11. after calling public abstract void ***.Subject.request()  

PS:這個結果的資訊非常重要,至少對我來說。因為我在動态代理犯暈的根源就在于将上面的subject.request()了解錯了,至少是被表面所迷惑,沒有發現這個subject和Proxy之間的聯系,一度糾結于最後調用的這個request()是怎麼和invoke()聯系上的,而invoke又是怎麼知道request存在的。其實上面的true和class $Proxy0就能解決很多的疑問,再加上下面将要說的$Proxy0的源碼,完全可以解決動态代理的疑惑了。  

        從以上代碼和結果可以看出,我們并沒有顯示的調用invoke()方法,但是這個方法确實執行了。下面就整個的過程進行分析一下: 

        從Client中的代碼看,可以從newProxyInstance這個方法作為突破口,我們先來看一下Proxy類中newProxyInstance方法的源代碼: 

Java代碼  

  1. public static Object newProxyInstance(ClassLoader loader,  
  2.         Class<?>[] interfaces,  
  3.         InvocationHandler h)  
  4. throws IllegalArgumentException  
  5. {  
  6.     if (h == null) {  
  7.         throw new NullPointerException();  
  8.     }  
  9.     Class cl = getProxyClass(loader, interfaces);  
  10.     try {  
  11.         Constructor cons = cl.getConstructor(constructorParams);  
  12.         return (Object) cons.newInstance(new Object[] { h });  
  13.     } catch (NoSuchMethodException e) {  
  14.         throw new InternalError(e.toString());  
  15.     } catch (IllegalAccessException e) {  
  16.         throw new InternalError(e.toString());  
  17.     } catch (InstantiationException e) {  
  18.         throw new InternalError(e.toString());  
  19.     } catch (InvocationTargetException e) {  
  20.         throw new InternalError(e.toString());  
  21.     }  
  22. }  

        Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下幾件事.

        (1)根據參數loader和interfaces調用方法 getProxyClass(loader, interfaces)建立代理類$Proxy0.$Proxy0類 實作了interfaces的接口,并繼承了Proxy類. 

        (2)執行個體化$Proxy0并在構造方法中把DynamicSubject傳過去,接着$Proxy0調用父類Proxy的構造器,為h指派,如下: 

Java代碼  

  1. class Proxy{  
  2.     InvocationHandler h=null;  
  3.     protected Proxy(InvocationHandler h) {  
  4.         this.h = h;  
  5.     }  
  6.     ...  
  7. }  

        來看一下這個繼承了Proxy的$Proxy0的源代碼: 

Java代碼  

  1. public final class $Proxy0 extends Proxy implements Subject {  
  2.     private static Method m1;  
  3.     private static Method m0;  
  4.     private static Method m3;  
  5.     private static Method m2;  
  6.     static {  
  7.         try {  
  8.             m1 = Class.forName("java.lang.Object").getMethod("equals",  
  9.                     new Class[] { Class.forName("java.lang.Object") });  
  10.             m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
  11.                     new Class[0]);  
  12.             m3 = Class.forName("***.RealSubject").getMethod("request",  
  13.                     new Class[0]);  
  14.             m2 = Class.forName("java.lang.Object").getMethod("toString",  
  15.                     new Class[0]);  
  16.         } catch (NoSuchMethodException nosuchmethodexception) {  
  17.             throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
  18.         } catch (ClassNotFoundException classnotfoundexception) {  
  19.             throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
  20.         }  
  21.     } //static  
  22.     public $Proxy0(InvocationHandler invocationhandler) {  
  23.         super(invocationhandler);  
  24.     }  
  25.     @Override  
  26.     public final boolean equals(Object obj) {  
  27.         try {  
  28.             return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();  
  29.         } catch (Throwable throwable) {  
  30.             throw new UndeclaredThrowableException(throwable);  
  31.         }  
  32.     }  
  33.     @Override  
  34.     public final int hashCode() {  
  35.         try {  
  36.             return ((Integer) super.h.invoke(this, m0, null)).intValue();  
  37.         } catch (Throwable throwable) {  
  38.             throw new UndeclaredThrowableException(throwable);  
  39.         }  
  40.     }  
  41.     public final void request() {  
  42.         try {  
  43.             super.h.invoke(this, m3, null);  
  44.             return;  
  45.         } catch (Error e) {  
  46.         } catch (Throwable throwable) {  
  47.             throw new UndeclaredThrowableException(throwable);  
  48.         }  
  49.     }  
  50.     @Override  
  51.     public final String toString() {  
  52.         try {  
  53.             return (String) super.h.invoke(this, m2, null);  
  54.         } catch (Throwable throwable) {  
  55.             throw new UndeclaredThrowableException(throwable);  
  56.         }  
  57.     }  
  58. }  

        接着把得到的$Proxy0執行個體強制轉換成Subject,并将引用賦給subject。當執行subject.request()方法時,就調用了$Proxy0類中的request()方法,進而調用父類Proxy中的h的invoke()方法.即InvocationHandler.invoke()。 

PS:1、需要說明的一點是,Proxy類中getProxyClass方法傳回的是Proxy的Class類。之是以說明,是因為我一開始犯了個低級錯誤,以為傳回的是“被代理類的Class類”- -!推薦看一下getProxyClass的源碼,很長=。= 

        2、從$Proxy0的源碼可以看出,動态代理類不僅代理了顯示定義的接口中的方法,而且還代理了java的根類Object中的繼承而來的equals()、hashcode()、toString()這三個方法,并且僅此三個方法。 

3、$Proxy0是在Proxy類中的getProxyClass方法中的ProxyGenerator生成的位元組碼,實體磁盤中沒有對應的類檔案。

原位址:http://paddy-w.iteye.com/blog/841798