Java 動態代理機制詳解
在學習Spring的時候,我們知道Spring主要有兩大思想,一個是IoC,另一個就是AOP,對於IoC,依賴注入就不用多說了,而對於Spring的核心AOP來說,我們不但要知道怎麼通過AOP來滿足的我們的功能,我們更需要學習的是其底層是怎麼樣的一個原理,而AOP的原理就是java的動態代理機制,所以本篇隨筆就是對java的動態機制進行一個回顧。
在java的動態代理機制中,有兩個重要的類或介面,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class),這一個類和介面是實現我們動態代理所必須用到的。首先我們先來看看java的API幫助文檔是怎麼樣對這兩個類進行描述的:
InvocationHandler:
InvocationHandler is the interface implemented by the invocation handler of a proxy instance. Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
每一個動態代理類都必須要實現InvocationHandler這個介面,並且每個代理類的實例都關聯到了一個handler,當我們通過代理對象調用一個方法的時候,這個方法的調用就會被轉發為由InvocationHandler這個介面的 invoke 方法來進行調用。我們來看看InvocationHandler這個介面的唯一一個方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
我們看到這個方法一共接受三個參數,那麼這三個參數分別代表什麼呢?
proxy:指代我們所代理的那個真實對象
method:指代的是我們所要調用真實對象的某個方法的Method對象
args:指代的是調用真實對象某個方法時接受的參數
如果不是很明白,等下通過一個實例會對這幾個參數進行更深的講解。
接下來我們來看看Proxy這個類:
Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.
Proxy這個類的作用就是用來動態創建一個代理對象的類,它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個方法:
public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException
Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.
這個方法的作用就是得到一個動態的代理對象,其接收三個參數,我們來看看這三個參數所代表的含義:
loader:一個ClassLoader對象,定義了由哪個ClassLoader對象來對生成的代理對象進行載入
interfaces:一個Interface對象的數組,表示的是我將要給我需要代理的對象提供一組什麼介面,如果我提供了一組介面給它,那麼這個代理對象就宣稱實現了該介面(多態),這樣我就能調用這組介面中的方法了
h:一個InvocationHandler對象,表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪一個InvocationHandler對象上
好了,在介紹完這兩個介面(類)以後,我們來通過一個實例來看看我們的動態代理模式是什麼樣的:
首先我們定義了一個Subject類型的介面,為其聲明了兩個方法:
public interface Subject{ public void rent(); public void hello(String str);}
接著,定義了一個類來實現這個介面,這個類就是我們的真實對象,RealSubject類:
下一步,我們就要定義一個動態代理類了,前面說個,每一個動態代理類都必須要實現 InvocationHandler 這個介面,因此我們這個動態代理類也不例外:
public class DynamicProxy implements InvocationHandler{ //這個就是我們要代理的真實對象 private Object subject; // 構造方法,給我們要代理的真實對象賦初值 public DynamicProxy(Object subject) { this.subject = subject; } @Override public Object invoke(Object object, Method method, Object[] args) throws Throwable { //在代理真實對象前我們可以添加一些自己的操作 System.out.println("before rent house"); System.out.println("Method:" + method); // 當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用 method.invoke(subject, args); //在代理真實對象後我們也可以添加一些自己的操作 System.out.println("after rent house"); return null; }}
最後,來看看我們的Client類:
public class Client{ public static void main(String[] args) { // 我們要代理的真實對象 Subject realSubject = new RealSubject(); // 我們要代理哪個真實對象,就將該對象傳進去,最後是通過該真實對象來調用其方法的 InvocationHandler handler = new DynamicProxy(realSubject); /* * 通過Proxy的newProxyInstance方法來創建我們的代理對象,我們來看看其三個參數 * 第一個參數 handler.getClass().getClassLoader() ,我們這裡使用handler這個類的ClassLoader對象來載入我們的代理對象 * 第二個參數realSubject.getClass().getInterfaces(),我們這裡為代理對象提供的介面是真實對象所實行的介面,表示我要代理的是該真實對象,這樣我就能調用這組介面中的方法了 * 第三個參數handler, 我們這裡將這個代理對象關聯到了上方的 InvocationHandler 這個對象上 */ Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject .getClass().getInterfaces(), handler); System.out.println(subject.getClass().getName()); subject.rent(); subject.hello("world"); }}
我們先來看看控制台的輸出:
Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject .getClass().getInterfaces(), handler);
可能我以為返回的這個代理對象會是Subject類型的對象,或者是InvocationHandler的對象,結果卻不是,首先我們解釋一下為什麼我們這裡可以將其轉化為Subject類型的對象?原因就是在newProxyInstance這個方法的第二個參數上,我們給這個代理對象提供了一組什麼介面,那麼我這個代理對象就會實現了這組介面,這個時候我們當然可以將這個代理對象強制類型轉化為這組介面中的任意一個,因為這裡的介面是Subject類型,所以就可以將其轉化為Subject類型了。
同時我們一定要記住,通過 Proxy.newProxyInstance 創建的代理對象是在jvm運行時動態生成的一個對象,它並不是我們的InvocationHandler類型,也不是我們定義的那組介面的類型,而是在運行是動態生成的一個對象,並且命名方式都是這樣的形式,以$開頭,proxy為中,最後一個數字表示對象的標號。
接著我們來看看這兩句
subject.rent();
subject.hello(「world」);
這裡是通過代理對象來調用實現的那種介面中的方法,這個時候程序就會跳轉到由這個代理對象關聯到的 handler 中的invoke方法去執行,而我們的這個 handler 對象又接受了一個 RealSubject類型的參數,表示我要代理的就是這個真實對象,所以此時就會調用 handler 中的invoke方法去執行:
public Object invoke(Object object, Method method, Object[] args) throws Throwable { //在代理真實對象前我們可以添加一些自己的操作 System.out.println("before rent house"); System.out.println("Method:" + method); // 當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用 method.invoke(subject, args); //在代理真實對象後我們也可以添加一些自己的操作 System.out.println("after rent house"); return null; }
我們看到,在真正通過代理對象來調用真實對象的方法的時候,我們可以在該方法前後添加自己的一些操作,同時我們看到我們的這個 method 對象是這樣的:
正好就是我們的Subject介面中的兩個方法,這也就證明了當我通過代理對象來調用方法的時候,起實際就是委託由其關聯到的 handler 對象的invoke方法中來調用,並不是自己來真實調用,而是通過代理的方式來調用的。
這就是我們的java動態代理機制。
本篇隨筆詳細的講解了java中的動態代理機制,這個知識點非常非常的重要,包括我們Spring的AOP其就是通過動態代理的機制實現的,所以我們必須要好好的理解動態代理的機制。
點擊展開全文
※純乾貨分享 Java必備基礎知識點
※想學java還不知道 這些怎麼行?
※Java的標準,規範-介面-interface
TAG:java學習吧 |
※Veritas Velocity數據副本管理技術、原理詳解
※Redis 內存淘汰機制詳解
※linux進程信息查詢命令lsof詳解
※ThreadLocal實現原理詳解
※教學筆記:Web應用伺服器Tomcat的架構設計及原理實現詳解
※Apache Spark 統一內存管理模型詳解
※Qorivva MPC56xx系列MCU的Flash加密解密原理與工程實現方法詳解
※OpenStack之Magnum容器編服務排引擎詳解
※Android應用開發之資料庫操作詳解
※代碼詳解:通過模擬API來理解TensorFlow
※eBay運營之best match規則詳解
※詳解分散式應用程序協調服務Zookeeper
※async/await使用深入詳解
※「詳解」WebSocket相關知識整理
※詳解glide中crossfade引發的默認圖變形
※OpenStack技術和實戰詳解
※OpenStack關鍵技術系列:KeyStone鑒權認證系統詳解
※可視化CapsNet,詳解Hinton等人提出的膠囊概念與原理
※Spring Cloud中的Eureka服務註冊與發現詳解
※詳解下一代Oculus Rift——Half Dome VR原型機