java動態(tài)代理機(jī)制--那些讓你面試脫穎而出的技能

retrofit是一個解耦性非常高的網(wǎng)絡(luò)請求框架,最近在研究的時候發(fā)現(xiàn)了動態(tài)代理這個非常強(qiáng)大且實(shí)用的技術(shù)无牵,這篇文章將作為retrofit的前置知識,讓大家認(rèn)識:動態(tài)代理有哪些應(yīng)用場景,什么是動態(tài)代理克懊,怎樣使用七蜘,它的局限性在什么地方?

動態(tài)代理的應(yīng)用場景

1. AOP—面向切面編程橡卤,程序解耦

簡言之當(dāng)你想要對一些類的內(nèi)部的一些方法,在執(zhí)行前和執(zhí)行后做一些共同的的操作柜与,而在方法中執(zhí)行個性化操作的時候--用動態(tài)代理嵌灰。在業(yè)務(wù)量龐大的時候能夠降低代碼量,增強(qiáng)可維護(hù)性伞鲫。

2. 想要自定義第三放類庫中的某些方法

我引用了一個第三方類庫,但他的一些方法不滿足我的需求柒瓣,我想自己重寫一下那幾個方法吠架,或在方法前后加一些特殊的操作--用動態(tài)代理。但需要注意的是傍药,這些方法有局限性魂仍,我會在稍后說明拣挪。

什么是動態(tài)代理

類圖

以上的圖太過于抽象菠劝,我們從生活中的例子開始切入。

假如你是一個大房東(被代理人)赶诊,你有很多套房子想要出租,而你覺得找租客太麻煩寓调,不愿意自己弄锄码,因而你找一個人來代理你(代理人),幫打理這些東西巍耗,而這個人(代理人也就是中介)在幫你出租房屋的時候?qū)δ闶杖∫恍┫鄳?yīng)的中介費(fèi)(對房屋出租的一些額外操作)。對于租客而言,中介就是房東驯耻,代理你做一些事情。

以上霎迫,就是一個代理的例子帘靡,而他為什么叫動態(tài)代理,“動態(tài)”兩個字體現(xiàn)在什么地方描姚?

我們可以這樣想,如果你的每一套房子你都請一個代理人幫你打理筒扒,每當(dāng)你想再出租一套房子的時候你得再請一個绊寻,這樣你會請很多的代理人悬秉,花費(fèi)高額的中介成本冰蘑,這可以看作常說的“靜態(tài)代理”。

但假如我們把所有的房子都交給一個中介來代理祠肥,讓他在多套房子之間動態(tài)的切換身份,幫你應(yīng)付每一個租客聋丝。這就是一個“動態(tài)代理”的過程工碾。動態(tài)代理的一大特點(diǎn)就是編譯階段沒有代理類在運(yùn)行時才生成代理類。

我們用一段代碼來看一下

房屋出租的操作
/**
*定義一個借口
**/
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 {

// 這個就是我們要代理的真實(shí)對象渊额,即房東
private Object subject;

//  構(gòu)造方法,給我們要代理的真實(shí)對象賦初值
public DynamicProxy(Object subject) {
    this.subject = subject;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //  在代理真實(shí)對象前我們可以添加一些自己的操作火惊,中介收取中介費(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)代理對象調(diào)用真實(shí)對象的方法時奔垦,其會自動的跳轉(zhuǎn)到代理對象關(guān)聯(lián)的handler對象的invoke方法來進(jìn)行調(diào)用
        method.invoke(subject, args);
    }

    //  在代理真實(shí)對象后我們也可以添加一些自己的操作
    System.out.println("after "+method.getName()+" house");

    return null;
}
}
客人
public class Client {
public static void main(String[] args)
{
    //    我們要代理的真實(shí)對象--房東
    HouseOwner houseOwner = new HouseOwner();

    //    我們要代理哪個真實(shí)對象,就將該對象傳進(jìn)去惶岭,最后是通過該真實(shí)對象來調(diào)用其方法的
    InvocationHandler handler = new DynamicProxy(houseOwner);

    /*
     * 通過Proxy的newProxyInstance方法來創(chuàng)建我們的代理對象犯眠,我們來看看其三個參數(shù)
     * 第一個參數(shù) handler.getClass().getClassLoader() ,我們這里使用handler這個類的ClassLoader對象來加載我們的代理對象
     * 第二個參數(shù)realSubject.getClass().getInterfaces()鸯旁,我們這里為代理對象提供的接口是真實(shí)對象所實(shí)行的接口量蕊,表示我要代理的是該真實(shí)對象铺罢,這樣我就能調(diào)用這組接口中的方法了
     * 第三個參數(shù)handler危融, 我們這里將這個代理對象關(guān)聯(lián)到了上方的 InvocationHandler 這個對象上
     */
    RentHouse rentHouse = (RentHouse) Proxy.newProxyInstance(handler.getClass().getClassLoader(), houseOwner
            .getClass().getInterfaces(), handler);//一個動態(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í)行。

剛開始看代碼你可能會有很多疑惑烘贴,我們通過以下的內(nèi)容來看看動態(tài)代理應(yīng)該怎么用撮胧。

動態(tài)代理該如何使用

在java的動態(tài)代理機(jī)制中,有兩個重要的類和接口芹啥,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class)汽纠,這一個類和接口是實(shí)現(xiàn)我們動態(tài)代理所必須用到的傀履。

每一個動態(tài)代理類都必須要實(shí)現(xiàn)InvocationHandler這個接口(代碼中的中介),并且每個代理類的實(shí)例都關(guān)聯(lián)到了一個handler钓账,當(dāng)我們通過代理對象調(diào)用一個方法的時候,這個方法的調(diào)用就會被轉(zhuǎn)發(fā)為由InvocationHandler這個接口的 invoke(對方法的增強(qiáng)就寫在這里面) 方法來進(jìn)行調(diào)用。

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

我們看到這個方法一共接受三個參數(shù)羞福,那么這三個參數(shù)分別代表什么呢?

Object invoke(Object proxy, Method method, Object[] args) throws Throwable
//proxy:  指代我們所代理的那個真實(shí)對象
//method:  指代的是我們所要調(diào)用真實(shí)對象的某個方法的Method對象
//args:  指代的是調(diào)用真實(shí)對象某個方法時接受的參數(shù)

接下來我們來看看Proxy這個類

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException

Proxy這個類的作用就是用來動態(tài)創(chuàng)建一個代理對象的類卖陵,它提供了許多的方法张峰,但是我們用的最多的就是 newProxyInstance 這個方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException

這個方法的作用就是得到一個動態(tài)的代理對象,其接收三個參數(shù)撩荣,我們來看看這三個參數(shù)所代表的含義

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

//loader:  一個ClassLoader對象,定義了由哪個ClassLoader對象來對生成的代理對象進(jìn)行加載
//interfaces:  一個Interface對象的數(shù)組餐曹,表示的是我將要給我需要代理的對象提供一組什么接口,如果我提供了一組接口給它朽合,那么這個代理對象就宣稱實(shí)現(xiàn)了該接口(多態(tài))饱狂,這樣我就能調(diào)用這組接口中的方法了
//h:  一個InvocationHandler對象,表示的是當(dāng)我這個動態(tài)代理對象在調(diào)用方法的時候休讳,會關(guān)聯(lián)到哪一個InvocationHandler對象上

這樣一來,結(jié)合上面給出的代碼磺樱,我們就可以明白動態(tài)代理的使用方法了

動態(tài)代理的局限性

從動態(tài)代理的使用方法中我們看到其實(shí)可以被增強(qiáng)的方法都是實(shí)現(xiàn)了借口的(不實(shí)現(xiàn)借口的public方法也可以通過繼承被代理類來使用)婆咸,代碼中的HouseOwner繼承了RentHouse 。而對于private方法JDK的動態(tài)代理無能為力尚骄!
以上的動態(tài)代理是JDK的,對于java工程還有大名鼎鼎的CGLib憨闰,但遺憾的是CGLib并不能在android中使用需五,android虛擬機(jī)相對與jvm還是有區(qū)別的。

結(jié)束語

動態(tài)代理的使用場景遠(yuǎn)不止這些宏邮,內(nèi)部原理會在以后的文章中介紹,但應(yīng)用類反射臨時生成代理類這一機(jī)制決定它對性能會有一定的影響械筛。本文作為retrofit原理的前置文章并沒有太過詳盡飒炎,如有疏漏和錯誤,歡迎指正郎汪!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末闯狱,一起剝皮案震驚了整個濱河市砍鸠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌录豺,老刑警劉巖饭弓,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異弟断,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)昏翰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門刘急,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人统求,你說我怎么就攤上這事据块。” “怎么了另假?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長边篮。 經(jīng)常有香客問我开睡,道長,這世上最難降的妖魔是什么苟耻? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮扶檐,結(jié)果婚禮上凶杖,老公的妹妹穿的比我還像新娘。我一直安慰自己款筑,他們只是感情好智蝠,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布腾么。 她就那樣靜靜地躺著,像睡著了一般杈湾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上漆撞,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天殴泰,我揣著相機(jī)與錄音,去河邊找鬼浮驳。 笑死悍汛,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的至会。 我是一名探鬼主播离咐,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼奉件!你這毒婦竟也來了宵蛀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤县貌,失蹤者是張志新(化名)和其女友劉穎术陶,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體窃这,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瞳别,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了杭攻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祟敛。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖兆解,靈堂內(nèi)的尸體忽然破棺而出馆铁,到底是詐尸還是另有隱情,我是刑警寧澤锅睛,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布埠巨,位于F島的核電站,受9級特大地震影響现拒,放射性物質(zhì)發(fā)生泄漏辣垒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一印蔬、第九天 我趴在偏房一處隱蔽的房頂上張望勋桶。 院中可真熱鬧,春花似錦、人聲如沸例驹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鹃锈。三九已至荤胁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間屎债,已是汗流浹背仅政。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留扔茅,地道東北人已旧。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像召娜,于是被迫代替她去往敵國和親运褪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,520評論 25 707
  • 整體Retrofit內(nèi)容如下: 1玖瘸、Retrofit解析1之前哨站——理解RESTful 2秸讹、Retrofit解析...
    隔壁老李頭閱讀 3,221評論 2 10
  • Java代理和動態(tài)代理機(jī)制分析和應(yīng)用 概述 代理是一種常用的設(shè)計模式,其目的就是為其他對象提供一個代理以控制對某個...
    丸_子閱讀 3,009評論 6 57
  • 香九齡雅倒,能溫席.孝于親璃诀,所當(dāng)執(zhí). 黃香九歲的時候就懂得孝順父母,在冬天嚴(yán)冬之時用自己身體將父母枕席溫暖后再請父母安...
    歐陽寒耘閱讀 182評論 0 0
  • 世間愛蔑匣,有很多種劣欢,比如:愛情,友情裁良,師生情;但有一種愛是你生來享有凿将,離開卻依然存在的,那便是親情价脾。 我從小...
    塵一沐閱讀 386評論 0 0