沙漠中的魚

          欲上天堂,先下地獄
          posts - 0, comments - 56, trackbacks - 0, articles - 119
            BlogJava :: 首頁 ::  :: 聯系 :: 聚合  :: 管理

          關于InvocationHandler接口

          Posted on 2009-05-16 18:53 沙漠中的魚 閱讀(8250) 評論(0)  編輯  收藏 所屬分類: Java基礎
                 動態代理是很多框架和技術的基礎, spring 的AOP實現就是基于動態代理實現的。了解動態代理的機制對于理解AOP的底層實現是很有幫助的。

                 查看doc文檔就可以知道,在java.lang.reflect包中有一個叫Proxy的類。下面是doc文檔對Proxy類的說明:

                 "A dynamic proxy class (simply referred to as a proxy class below) is a class that implements a list of interfaces specified at runtime when the class is created, with behavior as described below. A proxy interface is such an interface that is implemented by a proxy class. A proxy instance is an instance of a proxy class. Each proxy instance has an associated invocation handler object, which implements the interface InvocationHandler."

                  Proxy類的設計用到代理模式的設計思想,Proxy類對象實現了代理目標的所有接口,并代替目標對象進行實際的操作。但這種替代不是一種簡單的替代,這樣沒有任何意義,代理的目的是在目標對象方法的基礎上作增強,這種增強的本質通常就是對目標對象的方法進行攔截。所以,Proxy應該包括一個方法攔截器,來指示當攔截到方法調用時作何種處理。InvocationHandler就是攔截器的接口。

                InvocationHandler接口也是在java.lang.reflec

               Object invoke(Object proxy, Method method, Object[] args)

               這個接口有三個參數,其中第二和第三個參數都比較好理解,一個是被攔截的方法,一個是該方法的參數列表。關鍵是第一個參數。按照doc文檔的解析,

                proxy - the proxy instance that the method was invoked on

                也就是說,proxy應該是一個代理實例,但為什么要傳入這個參數呢?

                帶著這個問題,自己編了個小程序作了一點試驗。

          ///////////////////////////////////////

                public interface IAnimal {
                     void info();
                }

          ////////////////////////////////////

              public class Dog implements IAnimal

              {

                    public void info() {
                       System.out.println("I am a dog!");
                    }
              }

          ///////////////////////////////////////
          import java.lang.reflect.*;

          public class ProxyTest {
           public static void main(String[] args) throws InterruptedException {
            final IAnimal animal = new Dog();
            Object proxyObj =Proxy.newProxyInstance(
              animal.getClass().getClassLoader(),
              animal.getClass().getInterfaces(),
              new InvocationHandler()
              {
               public Object invoke(Object proxy, Method method, Object[] args)
               {
                try {
                 System.out.println("被攔截的方法:" + method.getName());
                 return method.invoke(animal, args);
                }
                catch (IllegalArgumentException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
                 return null;
                } catch (IllegalAccessException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
                 return null;
                } catch (InvocationTargetException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
                 return null;
                }
               }
              });
            if(proxyObj instanceof IAnimal)
            {
             System.out.println("the proxyObj is an animal!");
            }
            else
            {
             System.out.println("the proxyObj isn't an animal!");
            }
            
            if(proxyObj instanceof Dog)
            {
             System.out.println("the proxyObj is a dog!");
            }
            else
            {
             System.out.println("the proxyObj isn't a dog!");
            }
            
            IAnimal animalProxy = (IAnimal)proxyObj;
            animalProxy.info();
            animalProxy.hashCode();
            System.out.println(animalProxy.getClass().getName().toString());
           }
          }

          程序執行的結果如下:

          the proxyObj is an animal!
          the proxyObj isn't a dog!
          被攔截的方法:info
          I am a dog!
          被攔截的方法:hashCode
          $Proxy0

          從結果可以看出以下幾點:

          1. proxyObj 是一個實現了目標對象接口的對象,而不同于目標對象。也就是說,這種代理機制是面向接口,而不是面向類的。

          2. info方法(在接口中)被成功攔截了,hashCode方法也成功被攔截了,但意外的是,getClass方法(繼承自Object 類的方法)并沒有被攔截!!

          3. 應用調試還可以看出Invocation接口中invoke方法的傳入的proxy參數確實就是代理對象實例proxyObj

          為何getClass()沒有被攔截?proxy參數又有何用呢?

          先不管,做一個試驗看看。既然這個proxy參數就是代理實例對象,它理所當然和proxyObj是一樣的,可以調用info等方法。于是我們可以在invoke方法中加上如下一條語句:

          ((IAnimal)proxy).info();

          結果是:

          the proxyObj is an animal!
          the proxyObj isn't a dog!
          被攔截的方法:info
          被攔截的方法:info

          .......

          被攔截的方法:info
          被攔截的方法:info

          然后就是棧溢出

          結果是很明顯的,在invoke方法中調用proxy中的方法會再一次引發invoke方法,這就陷入了死循環,最終結果當然是棧溢出的。

          可以在invoke方法中調用proxy.getClass(), 程序可以正常運行。但如果調用hashCode()方法同樣會導致棧溢出。

                 通過上面的試驗,可以得出一些初步結論,invoke 接口中的proxy參數不能用于調用所實現接口的方法。奇怪的是hashCode()和getClass()方法都是從Object中繼承下來的方法,為什么一個可以另一個不可以呢?帶首疑問到doc文檔看一下Object中這兩個方法,發現getClass()是定義為final的,而hashCode()不是。難道是這個原因,于是找到一個非final方法,如equals試了一下,真的又會導致棧溢出;找另一個final方法如wait(),試了一下,invoke又不攔截了。final 難道就是關鍵之處?

                還有一個問題就是proxy有什么用?既然proxy可以調用getClass()方法,我們就可以得到proxy的Class類象,從而可以獲得關于proxy代理實例的所有類信息,如方法列表,Annotation等,這就為我們提供的一個分析proxy的有力工具,如通過分析Annotation分析方法的聲明式事務需求。我想傳入proxy參數應該是這樣一個用意吧。

           

          InvocationHandler的資料

          屬于包:java.lang.reflect

          public interface InvocationHandler

          InvocationHandler 是代理實例的調用處理程序 實現的接口。

          每個代碼實例都具有一個關聯的調用處理程序。對代理實例調用方法時,將對方法調用進行編碼并將其指派到它的調用處理程序的 invoke 方法。

          從以下版本開始:
          JDK1.3

          invoke

          Object invoke(Object proxy,
          Method method,
          Object[] args)
          throws Throwable
          在代理實例上處理方法調用并返回結果。在與方法關聯的代理實例上調用方法時,將在調用處理程序上調用此方法。

           

          參數:
          proxy - 在其上調用方法的代理實例
          method - 對應于在代理實例上調用的接口方法的 Method 實例。Method 對象的聲明類將是在其中聲明方法的接口,該接口可以是代理類賴以繼承方法的代理接口的超接口。
          args - 包含傳入代理實例上方法調用的參數值的對象數組,如果接口方法不使用參數,則為 null。基本類型的參數被包裝在適當基本包裝器類(如 java.lang.Integerjava.lang.Boolean)的實例中。
          返回:
          從代理實例的方法調用返回的值。如果接口方法的聲明返回類型是基本類型,則此方法返回的值一定是相應基本包裝對象類的實例;否則,它一定是可分配到聲明返回類型的類型。如果此方法返回的值為 null 并且接口方法的返回類型是基本類型,則代理實例上的方法調用將拋出 NullPointerException。否則,如果此方法返回的值與上述接口方法的聲明返回類型不兼容,則代理實例上的方法調用將拋出 ClassCastException
          拋出:
          Throwable - 從代理實例上的方法調用拋出的異常。該異常的類型必須可以分配到在接口方法的 throws 子句中聲明的任一異常類型或未經檢查的異常類型 java.lang.RuntimeExceptionjava.lang.Error。如果此方法拋出經過檢查的異常,該異常不可分配到在接口方法的 throws 子句中聲明的任一異常類型,代理實例的方法調用將拋出包含此方法曾拋出的異常的 UndeclaredThrowableException
          主站蜘蛛池模板: 丹棱县| 肥东县| 阿克苏市| 郎溪县| 永城市| 准格尔旗| 屏山县| 门源| 怀柔区| 满洲里市| 闵行区| 新巴尔虎左旗| 临城县| 黑山县| 张家港市| 凤城市| 中超| 安岳县| 上犹县| 怀来县| 保山市| 普宁市| 方城县| 湖州市| 长寿区| 松原市| 肇庆市| 南皮县| 全南县| 锦屏县| 抚宁县| 丽水市| 衡南县| 左贡县| 冀州市| 焉耆| 土默特右旗| 临夏市| 高邑县| 甘谷县| 长春市|