retrofit是一個(gè)解耦性非常高的網(wǎng)絡(luò)請(qǐng)求框架,最近在研究的時(shí)候發(fā)現(xiàn)了動(dòng)態(tài)代理這個(gè)非常強(qiáng)大且實(shí)用的技術(shù),這篇文章將作為retrofit的前置知識(shí),讓大家認(rèn)識(shí):動(dòng)態(tài)代理有哪些應(yīng)用場景,什么是動(dòng)態(tài)代理,怎樣使用,它的局限性在什么地方?
創(chuàng)新互聯(lián)長期為1000多家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺(tái),與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為江油企業(yè)提供專業(yè)的成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站,江油網(wǎng)站改版等技術(shù)服務(wù)。擁有十載豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。
動(dòng)態(tài)代理的應(yīng)用場景
1. AOP—面向切面編程,程序解耦
簡言之當(dāng)你想要對(duì)一些類的內(nèi)部的一些方法,在執(zhí)行前和執(zhí)行后做一些共同的的操作,而在方法中執(zhí)行個(gè)性化操作的時(shí)候--用動(dòng)態(tài)代理。在業(yè)務(wù)量龐大的時(shí)候能夠降低代碼量,增強(qiáng)可維護(hù)性。
2. 想要自定義第三放類庫中的某些方法
我引用了一個(gè)第三方類庫,但他的一些方法不滿足我的需求,我想自己重寫一下那幾個(gè)方法,或在方法前后加一些特殊的操作--用動(dòng)態(tài)代理。但需要注意的是,這些方法有局限性,我會(huì)在稍后說明。
什么是動(dòng)態(tài)代理
以上的圖太過于抽象,我們從生活中的例子開始切入。
假如你是一個(gè)大房東(被代理人),你有很多套房子想要出租,而你覺得找租客太麻煩,不愿意自己弄,因而你找一個(gè)人來代理你(代理人),幫打理這些東西,而這個(gè)人(代理人也就是中介)在幫你出租房屋的時(shí)候?qū)δ闶杖∫恍┫鄳?yīng)的中介費(fèi)(對(duì)房屋出租的一些額外操作)。對(duì)于租客而言,中介就是房東,代理你做一些事情。
以上,就是一個(gè)代理的例子,而他為什么叫動(dòng)態(tài)代理,“動(dòng)態(tài)”兩個(gè)字體現(xiàn)在什么地方?
我們可以這樣想,如果你的每一套房子你都請(qǐng)一個(gè)代理人幫你打理,每當(dāng)你想再出租一套房子的時(shí)候你得再請(qǐng)一個(gè),這樣你會(huì)請(qǐng)很多的代理人,花費(fèi)高額的中介成本,這可以看作常說的“靜態(tài)代理”。
但假如我們把所有的房子都交給一個(gè)中介來代理,讓他在多套房子之間動(dòng)態(tài)的切換身份,幫你應(yīng)付每一個(gè)租客。這就是一個(gè)“動(dòng)態(tài)代理”的過程。動(dòng)態(tài)代理的一大特點(diǎn)就是編譯階段沒有代理類在運(yùn)行時(shí)才生成代理類。
我們用一段代碼來看一下
房屋出租的操作
/** *定義一個(gè)借口 **/ public interface RentHouse { void rent();//房屋出租 void charge(String str);//出租費(fèi)用收取 }
房東
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 { // 這個(gè)就是我們要代理的真實(shí)對(duì)象,即房東 private Object subject; // 構(gòu)造方法,給我們要代理的真實(shí)對(duì)象賦初值 public DynamicProxy(Object subject) { this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 在代理真實(shí)對(duì)象前我們可以添加一些自己的操作,中介收取中介費(fèi) System.out.println("before "+method.getName()+" house"); System.out.println("Method:" + method.getName()); // 如果方法是 charge 則中介收取100元中介費(fèi) if (method.getName().equals("charge")) { method.invoke(subject, args); System.out.println("I will get 100 RMB ProxyCharge."); } else { // 當(dāng)代理對(duì)象調(diào)用真實(shí)對(duì)象的方法時(shí),其會(huì)自動(dòng)的跳轉(zhuǎn)到代理對(duì)象關(guān)聯(lián)的handler對(duì)象的invoke方法來進(jìn)行調(diào)用 method.invoke(subject, args); } // 在代理真實(shí)對(duì)象后我們也可以添加一些自己的操作 System.out.println("after "+method.getName()+" house"); return null; } }
客人
public class Client { public static void main(String[] args) { // 我們要代理的真實(shí)對(duì)象--房東 HouseOwner houseOwner = new HouseOwner(); // 我們要代理哪個(gè)真實(shí)對(duì)象,就將該對(duì)象傳進(jìn)去,最后是通過該真實(shí)對(duì)象來調(diào)用其方法的 InvocationHandler handler = new DynamicProxy(houseOwner); /* * 通過Proxy的newProxyInstance方法來創(chuàng)建我們的代理對(duì)象,我們來看看其三個(gè)參數(shù) * 第一個(gè)參數(shù) handler.getClass().getClassLoader() ,我們這里使用handler這個(gè)類的ClassLoader對(duì)象來加載我們的代理對(duì)象 * 第二個(gè)參數(shù)realSubject.getClass().getInterfaces(),我們這里為代理對(duì)象提供的接口是真實(shí)對(duì)象所實(shí)行的接口,表示我要代理的是該真實(shí)對(duì)象,這樣我就能調(diào)用這組接口中的方法了 * 第三個(gè)參數(shù)handler, 我們這里將這個(gè)代理對(duì)象關(guān)聯(lián)到了上方的 InvocationHandler 這個(gè)對(duì)象上 */ RentHouse rentHouse = (RentHouse) Proxy.newProxyInstance(handler.getClass().getClassLoader(), houseOwner .getClass().getInterfaces(), handler);//一個(gè)動(dòng)態(tài)代理類,中介 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塊的中介費(fèi),說明我們不僅可以增加操作,甚至可以替換該方法或者直接讓該方法不執(zhí)行。
剛開始看代碼你可能會(huì)有很多疑惑,我們通過以下的內(nèi)容來看看動(dòng)態(tài)代理應(yīng)該怎么用。
動(dòng)態(tài)代理該如何使用
在java的動(dòng)態(tài)代理機(jī)制中,有兩個(gè)重要的類和接口,一個(gè)是 InvocationHandler(Interface)、另一個(gè)則是 Proxy(Class),這一個(gè)類和接口是實(shí)現(xiàn)我們動(dòng)態(tài)代理所必須用到的。
每一個(gè)動(dòng)態(tài)代理類都必須要實(shí)現(xiàn)InvocationHandler這個(gè)接口(代碼中的中介),并且每個(gè)代理類的實(shí)例都關(guān)聯(lián)到了一個(gè)handler,當(dāng)我們通過代理對(duì)象調(diào)用一個(gè)方法的時(shí)候,這個(gè)方法的調(diào)用就會(huì)被轉(zhuǎn)發(fā)為由InvocationHandler這個(gè)接口的 invoke(對(duì)方法的增強(qiáng)就寫在這里面) 方法來進(jìn)行調(diào)用。
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
我們看到這個(gè)方法一共接受三個(gè)參數(shù),那么這三個(gè)參數(shù)分別代表什么呢?
Object invoke(Object proxy, Method method, Object[] args) throws Throwable //proxy: 指代我們所代理的那個(gè)真實(shí)對(duì)象 //method: 指代的是我們所要調(diào)用真實(shí)對(duì)象的某個(gè)方法的Method對(duì)象 //args: 指代的是調(diào)用真實(shí)對(duì)象某個(gè)方法時(shí)接受的參數(shù)
接下來我們來看看Proxy這個(gè)類
Proxy這個(gè)類的作用就是用來動(dòng)態(tài)創(chuàng)建一個(gè)代理對(duì)象的類,它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個(gè)方法:
這個(gè)方法的作用就是得到一個(gè)動(dòng)態(tài)的代理對(duì)象,其接收三個(gè)參數(shù),我們來看看這三個(gè)參數(shù)所代表的含義
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException //loader:一個(gè)ClassLoader對(duì)象,定義了由哪個(gè)ClassLoader對(duì)象來對(duì)生成的代理對(duì)象進(jìn)行加載 //interfaces:一個(gè)Interface對(duì)象的數(shù)組,表示的是我將要給我需要代理的對(duì)象提供一組什么接口,如果我提供了一組接口給它,那么這個(gè)代理對(duì)象就宣稱實(shí)現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這組接口中的方法了 //h:一個(gè)InvocationHandler對(duì)象,表示的是當(dāng)我這個(gè)動(dòng)態(tài)代理對(duì)象在調(diào)用方法的時(shí)候,會(huì)關(guān)聯(lián)到哪一個(gè)InvocationHandler對(duì)象上
這樣一來,結(jié)合上面給出的代碼,我們就可以明白動(dòng)態(tài)代理的使用方法了
動(dòng)態(tài)代理的局限性
從動(dòng)態(tài)代理的使用方法中我們看到其實(shí)可以被增強(qiáng)的方法都是實(shí)現(xiàn)了借口的(不實(shí)現(xiàn)借口的public方法也可以通過繼承被代理類來使用),代碼中的HouseOwner繼承了RentHouse 。而對(duì)于private方法JDK的動(dòng)態(tài)代理無能為力!
以上的動(dòng)態(tài)代理是JDK的,對(duì)于java工程還有大名鼎鼎的CGLib,但遺憾的是CGLib并不能在android中使用,android虛擬機(jī)相對(duì)與jvm還是有區(qū)別的。
結(jié)束語
動(dòng)態(tài)代理的使用場景遠(yuǎn)不止這些,內(nèi)部原理會(huì)在以后的文章中介紹,但應(yīng)用類反射臨時(shí)生成代理類這一機(jī)制決定它對(duì)性能會(huì)有一定的影響。本文作為retrofit原理的前置文章并沒有太過詳盡,如有疏漏和錯(cuò)誤,歡迎指正!
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
本文題目:深入理解java動(dòng)態(tài)代理機(jī)制
鏈接URL:http://www.rwnh.cn/article18/ghcjgp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、網(wǎng)站設(shè)計(jì)公司、微信公眾號(hào)、虛擬主機(jī)、關(guān)鍵詞優(yōu)化、ChatGPT
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)