學習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
歡迎關注本站公眾號,獲取更多信息