JAVA動態代理機制解析
1. 概述
首先,我們來思考如下兩個問題:
什麽是代理模式?為什麽要使用代理模式?
簡單總結一下,所謂的代理模式就是在原有的服務上多加一個占位,通過這個占位去控制服務的訪問。通過代理模式,一方面可以控制如何訪問真正的服務對象,提供額外服務;另一方面,有機會通過重寫一些類來滿足特定的需要。
在java的動態代理機制中,有兩個重要的類或接口,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class),這一個類和接口是實現動態代理所必須用到的。下面我們具體分析一下:
(1)InvocationHandler接口:當通過代理對象調用一個方法的時候,這個方法的調用就會被轉發為由InvocationHandler接口的 invoke 方法來進行調用。InvocationHanler的 invoke 方法如下:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
三個參數的具體含義如下:
proxy: 代理的真實對象
method: 調用真實對象的某個方法的Method對象
args: 調用真實對象某個方法時接收的參數
(2)Proxy類:這個類的作用就是用來動態創建一個代理對象的類,它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個方法,如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
這個方法的作用就是得到一個動態的代理對象,其接收三個參數,我們來看看這三個參數所代表的含義:
loader: 一個ClassLoader對象,定義了由哪個ClassLoader對象來對生成的代理對象進行加載;
interfaces: 一個Interface對象的數組,表示的是我將要給我需要代理的對象提供一組什麽接口,如果我提供了一組接口給它,那麽這個代理對象就宣稱實現了該接口(多態),這樣我就能調用這組接口中的方法了;
h: 一個InvocationHandler對象,表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪一個InvocationHandler對象上
2. 示例
(1)我們先定義一個接口:
public interface Library { void isAvalid(); void sayHello(String name); }
(2)接口的實現類:
public class Book implements Library { @Override public void isAvalid() { System.out.println("This book can be borrowed! "); } @Override public void sayHello(String name) { System.out.println("Book name is: "+ name); } }
(3)動態代理:
public class DynamicProxy implements InvocationHandler { private Object object; public DynamicProxy(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Bollow flow begin..."); System.out.println("Method name is: "+ method); method.invoke(object, args); System.out.println("Bollow flow end..."); return null; } }
(4)測試類:
public class Reader { public static void main(String[] args) { Book book = new Book(); InvocationHandler handler = new DynamicProxy(book); Library library = (Library) Proxy.newProxyInstance(handler.getClass().getClassLoader(), book.getClass().getInterfaces(), handler); System.out.println(library.getClass().getName()); library.isAvalid(); library.sayHello("<JAVA Concurrency In Practice>"); } } 結果輸出: $Proxy0 Bollow flow begin... Method name is: public abstract void com.cn.dynamicProxy.Library.isAvalid() This book can be borrowed! Bollow flow end... Bollow flow begin... Method name is: public abstract void com.cn.dynamicProxy.Library.sayHello(java.lang.String) Book name is: <JAVA Concurrency In Practice> Bollow flow end...
說明:
通過 Proxy.newProxyInstance 創建的代理對象是在jvm運行時動態生成的一個對象,它並不是我們的InvocationHandler類型,也不是我們定義的那組接口的類型,而是在運行是動態生成的一個對象,並且命名方式都是這樣的形式,以$開頭,proxy為中,最後一個數字表示對象的標號。
實際的運行方法:
Method name is: public abstract void com.cn.dynamicProxy.Library.isAvalid()
Method name is: public abstract void com.cn.dynamicProxy.Library.sayHello(java.lang.String)
所以,當我通過代理對象來調用方法的時候,實際上就是委托由其關聯到的 handler 對象的invoke方法中來調用,並不是自己來真實調用,而是通過代理的方式來調用的。這就是java的動態代理機制。
JAVA動態代理機制解析