1. 程式人生 > >Java動態代理機制小結

Java動態代理機制小結

在java的動態代理機制中,有兩個重要的類或介面,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class),這一個類和介面是實現我們動態代理所必須用到的。首先我們先來看看java的API幫助文件是怎麼樣對這兩個類進行描述的:

InvocationHandler:


大概的意思應該是:

每一個動態代理類都必須要實現InvocationHandler這個介面,並且每個代理類的例項都關聯到了一個handler,當我們通過代理物件呼叫一個方法的時候,這個方法的呼叫就會被轉發為由InvocationHandler這個介面的 invoke 方法來進行呼叫。我們來看看InvocationHandler這個介面的唯一一個方法 

invoke 方法:

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

我們看到這個方法一共接受三個引數,那麼這三個引數分別代表什麼呢?

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

proxy:  指代我們所代理的那個真實物件
method:  指代的是我們所要呼叫真實物件的某個方法的Method物件
args:  指代的是呼叫真實物件某個方法時接受的引數

接下來我們來看看Proxy這個類:


Proxy這個類的作用就是用來動態建立一個代理物件的類,它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個方法:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException


這個方法的作用就是得到一個動態的代理物件,其接收三個引數,我們來看看這三個引數所代表的含義:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

loader:  一個ClassLoader物件,定義了由哪個ClassLoader物件來對生成的代理物件進行載入

interfaces:  一個Interface物件的陣列,表示的是我將要給我需要代理的物件提供一組什麼介面,如果我提供了一組介面給它,那麼這個代理物件就宣稱實現了該介面(多型),這樣我就能呼叫這組介面中的方法了

h:  一個InvocationHandler物件,表示的是當我這個動態代理物件在呼叫方法的時候,會關聯到哪一個InvocationHandler物件上

例項如下:

首先我們定義了一個Subject型別的介面,為其聲明瞭兩個方法:


接著,定義了一個類來實現這個介面,這個類就是我們的真實物件,RealSubject類:


下一步,我們就要定義一個動態代理類了,前面說個,每一個動態代理類都必須要實現 InvocationHandler 這個介面:


最後,來看看我們的Client類:


ClassLoader的簡單知識結,請看:

http://blog.csdn.net/weisg81/article/details/77415937

執行結果:


可能我以為返回的這個代理物件會是Subject型別的物件,或者是InvocationHandler的物件,結果卻不是,首先我們解釋一下為什麼我們這裡可以將其轉化為Subject型別的物件?原因就是在newProxyInstance這個方法的第二個引數上,我們給這個代理物件提供了一組什麼介面,那麼我這個代理物件就會實現了這組介面,這個時候我們當然可以將這個代理物件強制型別轉化為這組介面中的任意一個,因為這裡的介面是Subject型別,所以就可以將其轉化為Subject型別了

同時我們一定要記住,通過 Proxy.newProxyInstance 建立的代理物件是在jvm執行時動態生成的一個物件,它並不是我們的InvocationHandler型別,也不是我們定義的那組介面的型別,而是在執行是動態生成的一個物件,並且命名方式都是這樣的形式,以$開頭,proxy為中,最後一個數字表示物件的標號

接著我們來看看這兩句 

subject.rent();
subject.hello("world");

這裡是通過代理物件來呼叫實現的那種介面中的方法,這個時候程式就會跳轉到由這個代理物件關聯到的 handler 中的invoke方法去執行,而我們的這個 handler 物件又接受了一個 RealSubject型別的引數,表示我要代理的就是這個真實物件,所以此時就會呼叫 handler 中的invoke方法去執行。

當我通過代理物件來呼叫方法的時候,起實際就是委託由其關聯到的 handler 物件的invoke方法中來呼叫,並不是自己來真實呼叫,而是通過代理的方式來呼叫的。

這就是我們的java動態代理機制