學習AOP時遇到關於InvocationHandler接口的問題

動態代理是不少框架和技術的基礎, 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參數應該是這樣一個用意吧。

摘自:
  http://shizukyo.iteye.com/blog/245108
相關文章
相關標籤/搜索