1. 程式人生 > >JDK動態代理中關於InvocationHandler中invoke()方法的呼叫問題

JDK動態代理中關於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.     /* 
  10.      * Look up or generate the designated proxy class. 
  11.      */  
  12.     Class cl = getProxyClass(loader, interfaces);  
  13.     /* 
  14.      * Invoke its constructor with the designated invocation handler. 
  15.      */  
  16.     try {  
  17.            /* 
  18.             * Proxy原始碼開始有這樣的定義: 
  19.             * private final static Class[] constructorParams = { InvocationHandler.class }; 
  20.             * cons即是形參為InvocationHandler型別的構造方法 
  21.            */  
  22.         Constructor cons = cl.getConstructor(constructorParams);  
  23.         return (Object) cons.newInstance(new Object[] { h });  
  24.     } catch (NoSuchMethodException e) {  
  25.         throw new InternalError(e.toString());  
  26.     } catch (IllegalAccessException e) {  
  27.         throw new InternalError(e.toString());  
  28.     } catch (InstantiationException e) {  
  29.         throw new InternalError(e.toString());  
  30.     } catch (InvocationTargetException e) {  
  31.         throw new InternalError(e.toString());  
  32.     }  
  33. }  


        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.         }