Java動態代理機制——那些讓你面試脫穎而出的技能
retrofit是一個解耦性非常高的網路請求框架,最近在研究的時候發現了動態代理這個非常強大且實用的技術,這篇文章將作為retrofit的前置知識,讓大家認識:動態代理有哪些應用場景,什麼是動態代理,怎樣使用,它的侷限性在什麼地方?
#動態代理的應用場景
1. AOP—面向切面程式設計,程式解耦
簡言之當你想要對一些類的內部的一些方法,在執行前和執行後做一些共同的的操作,而在方法中執行個性化操作的時候--用動態代理。在業務量龐大的時候能夠降低程式碼量,增強可維護性。
2. 想要自定義第三放類庫中的某些方法
我引用了一個第三方類庫,但他的一些方法不滿足我的需求,我想自己重寫一下那幾個方法,或在方法前後加一些特殊的操作--用動態代理。但需要注意的是,這些方法有侷限性,我會在稍後說明。
什麼是動態代理
類圖
以上的圖太過於抽象,我們從生活中的例子開始切入。
假如你是一個大房東(被代理人),你有很多套房子想要出租,而你覺得找租客太麻煩,不願意自己弄,因而你找一個人來代理你(代理人),幫打理這些東西,而這個人(代理人也就是中介)在幫你出租房屋的時候對你收取一些相應的中介費(對房屋出租的一些額外操作)。對於租客而言,中介就是房東,代理你做一些事情。
以上,就是一個代理的例子,而他為什麼叫動態代理,“動態”兩個字型現在什麼地方?
我們可以這樣想,如果你的每一套房子你都請一個代理人幫你打理,每當你想再出租一套房子的時候你得再請一個,這樣你會請很多的代理人,花費高額的中介成本,這可以看作常說的“靜態代理”。
但假如我們把所有的房子都交給一箇中介來代理,讓他在多套房子之間動態的切換身份,幫你應付每一個租客。這就是一個“動態代理”的過程。動態代理的一大特點就是編譯階段沒有代理類在執行時才生成代理類。
我們用一段程式碼來看一下
房屋出租的操作
/**
*定義一個藉口
**/
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;
}
@Override
public 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原理的前置文章並沒有太過詳盡,如有疏漏和錯誤,歡迎指正!
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69920894/viewspace-2723131/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 那年,這個Spring大家族讓我在面試中脫穎而出Spring面試
- 學會這幾招,輕鬆讓你的github脫穎而出Github
- SEO人員如何在公司面試脫穎而出呢?面試
- Awareness Kit讓你的音樂APP脫穎而出,更懂使用者,也更動人心APP
- “賽馬”制度讓科研疆場的“千里馬”脫穎而出
- 帶你學 Java 技術之動態代理機制Java
- Java動態代理和反射機制Java反射
- 技術管理進階——如何脫穎而出?
- 用particles.js,讓你的網站背景特效瞬間脫穎而出,驚豔所有人JS網站特效
- 企業如何在AI時代脫穎而出AI
- 摘下 TGA 2019 “最佳多人遊戲”獎 是什麼讓《APEX英雄》脫穎而出?遊戲
- 區塊鏈社交APP如何在移動社交時代脫穎而出區塊鏈APP
- 一文學會 Java 動態代理機制Java
- Java進階 | Proxy動態代理機制詳解Java
- 讓我們打一場動態代理的官司–Java動態代理Java
- 冷門科普類自媒體如何才能脫穎而出
- openGauss 動態資料脫敏機制
- 2019全球PostgreSQL生態報告出爐,PG為何從RDBMS中脫穎而出?SQL
- 為什麼python在眾多程式語言中脫穎而出?Python
- openGauss-動態資料脫敏機制
- 大廠高階工程師面試必問系列:Java動態代理機制和實現原理詳解工程師面試Java
- Java的代理機制Java
- 基於 CGLIB 庫的動態代理機制CGLib
- 六大「未來式」儲存器,誰將脫穎而出?
- 想在職場脫穎而出,女性要掌握三大法寶
- 《英靈神殿》為何能在同類遊戲中脫穎而出?遊戲
- 好程式設計師Java教程Java動態代理機制詳解程式設計師Java
- 5項先進採購技術,幫助你的企業脫穎而出
- 深入理解JDK動態代理機制JDK
- 大廠林立 雲途騰為何在國網能源雲脫穎而出?
- TAPTAP憑什麼在眾多遊戲平臺中脫穎而出APT遊戲
- 逆境之中見真章,JASMINER在眾多出海企業中脫穎而出ASM
- 萬能碼如何脫穎而出?(安全掃碼專業委員會)
- 馬耳他在區塊鏈友好的司法管轄區中脫穎而出區塊鏈
- JAVA 靜態代理 & 動態代理Java
- Java代理(jdk靜態代理、動態代理和cglib動態代理)JavaJDKCGLib
- 好程式設計師Java培訓分享Java動態代理機制詳解程式設計師Java
- 冷飯盛行之下,為何「低清重製」作品能夠脫穎而出?