#動態代理的應用場景java
1. AOP—面向切面編程,程序解耦android
簡言之當你想要對一些類的內部的一些方法,在執行前和執行後作一些共同的的操做,而在方法中執行個性化操做的時候--用動態代理。在業務量龐大的時候可以下降代碼量,加強可維護性。程序員
2. 想要自定義第三放類庫中的某些方法面試
我引用了一個第三方類庫,但他的一些方法不知足個人需求,我想本身重寫一下那幾個方法,或在方法先後加一些特殊的操做--用動態代理。但須要注意的是,這些方法有侷限性,我會在稍後說明。編程
什麼是動態代理數組
以上的圖太過於抽象,咱們從生活中的例子開始切入。網絡
假如你是一個大房東(被代理人),你有不少套房子想要出租,而你以爲找租客太麻煩,不肯意本身弄,於是你找一我的來代理你(代理人),幫打理這些東西,而這我的(代理人也就是中介)在幫你出租房屋的時候對你收取一些相應的中介費(對房屋出租的一些額外操做)。對於租客而言,中介就是房東,代理你作一些事情。架構
以上,就是一個代理的例子,而他爲何叫動態代理,「動態」兩個字體如今什麼地方?框架
咱們能夠這樣想,若是你的每一套房子你都請一個代理人幫你打理,每當你想再出租一套房子的時候你得再請一個,這樣你會請不少的代理人,花費高額的中介成本,這能夠看做常說的「靜態代理」。jvm
但假如咱們把全部的房子都交給一箇中介來代理,讓他在多套房子之間動態的切換身份,幫你應付每個租客。這就是一個「動態代理」的過程。動態代理的一大特色就是編譯階段沒有代理類在運行時才生成代理類。
咱們用一段代碼來看一下
房屋出租的操做
/**
*定義一個藉口
**/public interface RentHouse {void rent();//房屋出租void charge(String str);//出租費用收取}
房東
public class HouseOwner implements RentHouse {public void rent() {
System.out.println("I want to rent my house");
}public void charge(String str) {
System.out.println("You get : " + str + " RMB HouseCharge.");
}
}
中介
public class DynamicProxy implements InvocationHandler {// 這個就是咱們要代理的真實對象,即房東private Object subject;// 構造方法,給咱們要代理的真實對象賦初值public DynamicProxy(Object subject) { this.subject = subject;
}
@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 在代理真實對象前咱們能夠添加一些本身的操做,中介收取中介費
System.out.println("before "+method.getName()+" house");
System.out.println("Method:" + method.getName()); // 若是方法是 charge 則中介收取100元中介費
if (method.getName().equals("charge")) {
method.invoke(subject, args);
System.out.println("I will get 100 RMB ProxyCharge.");
} else { // 當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用
method.invoke(subject, args);
} // 在代理真實對象後咱們也能夠添加一些本身的操做
System.out.println("after "+method.getName()+" house"); return null;
}
客人
public class Client {public static void main(String[] args){ // 咱們要代理的真實對象--房東
HouseOwner houseOwner = new HouseOwner(); // 咱們要代理哪一個真實對象,就將該對象傳進去,最後是經過該真實對象來調用其方法的
InvocationHandler handler = new DynamicProxy(houseOwner); /*
* 經過Proxy的newProxyInstance方法來建立咱們的代理對象,咱們來看看其三個參數
* 第一個參數 handler.getClass().getClassLoader() ,咱們這裏使用handler這個類的ClassLoader對象來加載咱們的代理對象
* 第二個參數realSubject.getClass().getInterfaces(),咱們這裏爲代理對象提供的接口是真實對象所實行的接口,表示我要代理的是該真實對象,這樣我就能調用這組接口中的方法了
* 第三個參數handler, 咱們這裏將這個代理對象關聯到了上方的 InvocationHandler 這個對象上
*/
RentHouse rentHouse = (RentHouse) Proxy.newProxyInstance(handler.getClass().getClassLoader(), houseOwner
.getClass().getInterfaces(), handler);//一個動態代理類,中介
System.out.println(rentHouse.getClass().getName());
rentHouse.rent();
rentHouse.charge("10000");
}
}
咱們來看一下輸出
com.sun.proxy.$Proxy0
before rent house
Method:rent
I want to rent my house
after rent house
before charge house
Method:charge
You get : 10000 RMB HouseCharge.
I will get 100 RMB ProxyCharge.
after charge house
Process finished with exit code 0
輸出裏有 before rent house以及after rent house,說明咱們能夠在方法的先後增長操做。再看輸出 I will get 100 RMB ProxyCharge. 中介收取了100塊的中介費,說明咱們不只能夠增長操做,甚至能夠替換該方法或者直接讓該方法不執行。
剛開始看代碼你可能會有不少疑惑,咱們經過如下的內容來看看動態代理應該怎麼用。
#動態代理該如何使用
在java的動態代理機制中,有兩個重要的類和接口,一個是 InvocationHandler(Interface)、另外一個則是 Proxy(Class),這一個類和接口是實現咱們動態代理所必須用到的。
每個動態代理類都必需要實現InvocationHandler這個接口(代碼中的中介),而且每一個代理類的實例都關聯到了一個handler,當咱們經過代理對象調用一個方法的時候,這個方法的調用就會被轉發爲由InvocationHandler這個接口的 invoke(對方法的加強就寫在這裏面) 方法來進行調用。
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這個類
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
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對象上
這樣一來,結合上面給出的代碼,咱們就能夠明白動態代理的使用方法了
#動態代理的侷限性
從動態代理的使用方法中咱們看到其實能夠被加強的方法都是實現了藉口的(不實現藉口的public方法也能夠經過繼承被代理類來使用),代碼中的HouseOwner繼承了RentHouse 。而對於private方法JDK的動態代理無能爲力!
以上的動態代理是JDK的,對於java工程還有大名鼎鼎的CGLib,但遺憾的是CGLib並不能在android中使用,android虛擬機相對與jvm仍是有區別的。
結束語
動態代理的使用場景遠不止這些,內部原理會在之後的文章中介紹,但應用類反射臨時生成代理類這一機制決定它對性能會有必定的影響。本文做爲retrofit原理的前置文章並無太過詳盡,若有疏漏和錯誤,歡迎指正!
若是你也想在IT行業拿高薪,能夠參加咱們的訓練營課程,選擇最適合本身的課程學習,技術大牛親授,7個月後,進入名企拿高薪。咱們的課程內容有:Java工程化、高性能及分佈式、高性能、深刻淺出。高架構。性能調優、Spring,MyBatis,Netty源碼分析和大數據等多個知識點。若是你想拿高薪的,想學習的,想就業前景好的,想跟別人競爭能取得優點的,想進阿里面試但擔憂面試不過的,你均可以來,羣號爲: 454377428
注:加羣要求
一、具備1-5工做經驗的,面對目前流行的技術不知從何下手,須要突破技術瓶頸的能夠加。
二、在公司待久了,過得很安逸,但跳槽時面試碰壁。須要在短期內進修、跳槽拿高薪的能夠加。
三、若是沒有工做經驗,但基礎很是紮實,對java工做機制,經常使用設計思想,經常使用java開發框架掌握熟練的,能夠加。
四、以爲本身很牛B,通常需求都能搞定。可是所學的知識點沒有系統化,很難在技術領域繼續突破的能夠加。
5.阿里Java高級大牛直播講解知識點,分享知識,多年工做經驗的梳理和總結,帶着你們全面、科學地創建本身的技術體系和技術認知!
6.小號或者小白之類加羣一概不給過,謝謝。
目標已經有了,下面就看行動了!記住:學習永遠是本身的事情,你不學時間也不會多,你學了有時候卻可以使用本身學到的知識換得更多自由自在的美好時光!時間是生命的基本組成部分,也是萬物存在的根本尺度,咱們的時間在那裏咱們的生活就在那裏!咱們價值也將在那裏提高或消弭!Java程序員,加油吧