java代理模式的那些事

java代理模式-登場(chǎng)

什么是代理模式多律?

代理模式是java中的一種設(shè)計(jì)模式南吮,它其實(shí)就是設(shè)置一個(gè)中間環(huán)節(jié)來代理你要對(duì)原目標(biāo)對(duì)象的訪問龄句。簡(jiǎn)言之傀蚌,代理模式就是有一個(gè)充當(dāng)代理者身份的類或方法來控制原對(duì)象的引用勋桶。

還是不太理解脱衙,你能舉個(gè)例子說明一下嗎?

這里一個(gè)很好的例子(引用鏈接): 一個(gè)公司是賣攝像頭的例驹,但公司不直接跟用戶打交道捐韩,而是通過代理商跟用戶打交道。如果:公司接口中有一個(gè)賣產(chǎn)品的方法眠饮,那么公司需要實(shí)現(xiàn)這個(gè)方法奥帘,而代理商也必須實(shí)現(xiàn)這個(gè)方法。如果公司賣多少錢仪召,代理商也賣多少錢寨蹋,那么代理商就賺不了錢。所以代理商在調(diào)用公司的賣方法后扔茅,加上自己的利潤(rùn)然后再把產(chǎn)品賣給客戶已旧。而客戶不直接跟公司打交道,或者客戶根本不知道公司的存在召娜,然而客戶最終卻買到了產(chǎn)品运褪。*

它用途應(yīng)該挺大的吧?

它是java中常用的設(shè)計(jì)模式之一玖瘸,并且在spring框架中有廣泛應(yīng)用(AOP)秸讹,它一般有三種模式:靜態(tài)代理、jdk1.6+中的動(dòng)態(tài)代理雅倒、cglib動(dòng)態(tài)代理璃诀。

java代理模式-靜態(tài)代理

首先,動(dòng)態(tài)代理是通過反射機(jī)制來處理相關(guān)業(yè)務(wù)方法蔑匣。那么你也猜到了劣欢,靜態(tài)代理則是通過編寫代理類來完成代理過程棕诵。

公司要賣的產(chǎn)品(實(shí)現(xiàn)接口)

package top.code666.porxy;
//公司中的業(yè)務(wù)接口
public interface ICompany {
    //賣攝像頭
    void sellCamera();
} 

公司的實(shí)現(xiàn)類(目標(biāo)類)

package top.code666.porxy;
//公司類
public class CompanyImpl implements ICompany{
    @Override
    public void sellCamera() {
        System.out.println("一個(gè)很好的Camera,高清無碼凿将。(xx公司生產(chǎn)Camera并出售給代理商校套,定價(jià)¥1000)");
    }
}

代理商的實(shí)現(xiàn)類(代理類)

package top.code666.porxy;

//代理類
public class ProxyImpl implements ICompany{
    private CompanyImpl ci;
    
    public ProxyImpl(CompanyImpl ci){
        this.ci = ci;
    }
    
    @Override
    public void sellCamera() {
        System.out.println("(賣前打了一波廣告)ss微商成為xx公司的最大代理商,現(xiàn)在正式售賣Camera了牧抵!不要998笛匙,不要98,只要9998你就可以把它帶回家~");
        ci.sellCamera();
        System.out.println("(賣后服務(wù))恭喜你購(gòu)買成功灭忠,三分鐘內(nèi)無條件退換哦~");
    }

}

客戶(測(cè)試類)

package top.code666.porxy;

//一個(gè)有錢的人
public class RichMan {
    public static void main(String[] args) {
        CompanyImpl ci = new CompanyImpl();
        ProxyImpl pi = new ProxyImpl(ci);
        pi.sellCamera();
    }
}

運(yùn)行結(jié)果

(賣前打了一波廣告)ss微商成為xx公司的最大代理商膳算,現(xiàn)在正式售賣Camera了!不要998弛作,不要98涕蜂,只要9998你就可以把它帶回家~
一個(gè)很好的Camera,高清無碼映琳。(xx公司生產(chǎn)Camera并出售給代理商机隙,定價(jià)¥1000)
(賣后服務(wù))恭喜你購(gòu)買成功,三分鐘內(nèi)無條件退換哦~

你現(xiàn)在也感覺到了萨西,靜態(tài)代理就是這么的簡(jiǎn)單有鹿。不過它卻存在許多弊端:

  1. 如果要代理的產(chǎn)品多了,那么你的整個(gè)項(xiàng)目將變得非常冗余谎脯。
  2. 如果要修改接口葱跋,那么你的目標(biāo)對(duì)象和代理對(duì)象都要修改,所以不易于維護(hù)源梭。

這時(shí)我們的動(dòng)態(tài)代理就可以很好的解決這些弊端了娱俺。燈登瞪等~ ,讓我們歡迎動(dòng)態(tài)代理閃亮登場(chǎng)吧(前方高能废麻,如果你對(duì)java的反射機(jī)制不是很了解荠卷,請(qǐng)先去學(xué)習(xí)反射相關(guān)知識(shí)后再過來)

java代理模式-動(dòng)態(tài)代理(JDK1.6+)

JDK1.6以上的版本自帶了動(dòng)態(tài)代理方法。我們只需要實(shí)現(xiàn)InvocationHandler就可以使用代理了烛愧。前提:你的目標(biāo)對(duì)象必須實(shí)現(xiàn)接口油宜,否則不能使用JDK動(dòng)態(tài)代理

接口與目標(biāo)類是與上一樣,所以這里不再重復(fù)

代理商的實(shí)現(xiàn)類(代理類)

package top.code666.jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

//代理對(duì)象
public class ProxyImpl implements InvocationHandler{
    //目標(biāo)對(duì)象
    private Object target;
    
    public ProxyImpl(Object target){
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("(賣前打了一波廣告)ss微商成為xx公司的最大代理商怜姿,現(xiàn)在正式售賣Camera了慎冤!不要998,不要98沧卢,只要9998你就可以把它帶回家~");
        Object result = method.invoke(target, args); // 調(diào)用目標(biāo)類的方法
        System.out.println("(賣后服務(wù))恭喜你購(gòu)買成功粪薛,三分鐘內(nèi)無條件退換哦~");
        return result;
    }

}

客戶(測(cè)試類)

package top.code666.jdkproxy;

import java.lang.reflect.Proxy;

//一個(gè)有錢的人
public class RichMan {
    public static void main(String[] args) {
        ICompany ic = new CompanyImpl();
        ProxyImpl pi = new ProxyImpl(ic); //傳入目標(biāo)對(duì)象
        ICompany proxySubject = (ICompany) Proxy.newProxyInstance(
                CompanyImpl.class.getClassLoader(), 
                CompanyImpl.class.getInterfaces(), pi); // new代理實(shí)例
        proxySubject.sellCamera();
    }
}

可以發(fā)現(xiàn),在JDK的動(dòng)態(tài)代理中搏恤,我們的代理類是不需要再重復(fù)寫了的违寿,可以共用同一個(gè)。但是它需要定義接口熟空,然后才能實(shí)現(xiàn)代理功能藤巢,所以還是存在一定的局限性。下面讓我們來看看CGLIB是怎么實(shí)現(xiàn)代理功能的吧息罗,它會(huì)不會(huì)不需要就能實(shí)現(xiàn)呢掂咒?

java代理模式-動(dòng)態(tài)代理(cglib.jar)

cglib可以在目標(biāo)類不實(shí)現(xiàn)接口方法時(shí),也能夠給這樣的類提供動(dòng)態(tài)代理迈喉。而JDK中是不行的绍刮。Spring在給某個(gè)類提供動(dòng)態(tài)代理時(shí)會(huì)自動(dòng)在JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理中動(dòng)態(tài)的選擇。(其實(shí)JDK中的動(dòng)態(tài)代理是要比CGLIB中動(dòng)態(tài)代理效率要稍微高一點(diǎn)點(diǎn)的)

使用cglib需要導(dǎo)入cglib.jar+asm.jar

目標(biāo)類(因?yàn)槭侵苯訌?fù)制的挨摸,類名啥的我就沒改了孩革,Impl或許對(duì)一些人來說有點(diǎn)礙眼……)

package top.code666.cglib;

//公司類
public class CompanyImpl{
    
    public void sellCamera() {
        System.out.println("一個(gè)很好的Camera毙芜,高清無碼钢拧。(xx公司生產(chǎn)Camera并出售給代理商淑际,定價(jià)¥1000)");
    }

}

代理類

package top.code666.cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

//代理對(duì)象
public class ProxyImpl implements MethodInterceptor{

    @Override
    public Object intercept(Object arg0, Method arg1, Object[] arg2,
            MethodProxy arg3) throws Throwable {
        System.out.println("(賣前打了一波廣告)ss微商成為xx公司的最大代理商探遵,現(xiàn)在正式售賣Camera了奥邮!不要998钙勃,不要98叭披,只要9998你就可以把它帶回家~");
        Object result = arg3.invokeSuper(arg0, arg2);
        System.out.println("(賣后服務(wù))恭喜你購(gòu)買成功颅筋,三分鐘內(nèi)無條件退換哦~");
        return result;
    }
}

測(cè)試類

package top.code666.cglib;

import net.sf.cglib.proxy.Enhancer;

//一個(gè)有錢的人
public class RichMan {
    public static void  main(String ... args) {
        CompanyImpl target = new CompanyImpl();
        RichMan test = new RichMan();
        CompanyImpl proxyTarget = (CompanyImpl) test.createProxy(CompanyImpl.class);
        proxyTarget.sellCamera();
    }

    public Object createProxy(Class targetClass) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(new ProxyImpl());
        return enhancer.create();
    }
}

JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理均是實(shí)現(xiàn)Spring AOP的基礎(chǔ)置逻。

代理對(duì)象的生成過程由Enhancer類實(shí)現(xiàn)推沸,大概步驟如下:

  1. 生成代理類Class的二進(jìn)制字節(jié)碼;
  2. 通過Class.forName加載二進(jìn)制字節(jié)碼券坞,生成Class對(duì)象鬓催;
  3. 通過反射機(jī)制獲取實(shí)例構(gòu)造,并初始化代理類對(duì)象报慕。

總結(jié)

  1. 靜態(tài)代理實(shí)現(xiàn)較簡(jiǎn)單深浮,只要代理對(duì)象對(duì)目標(biāo)對(duì)象進(jìn)行包裝,即可實(shí)現(xiàn)增強(qiáng)功能眠冈,但靜態(tài)代理只能為一個(gè)目標(biāo)對(duì)象服務(wù)飞苇,如果目標(biāo)對(duì)象過多,則會(huì)產(chǎn)生很多代理類蜗顽。
  2. JDK動(dòng)態(tài)代理需要目標(biāo)對(duì)象實(shí)現(xiàn)業(yè)務(wù)接口布卡,代理類只需實(shí)現(xiàn)InvocationHandler接口。
  3. 動(dòng)態(tài)代理生成的類為 class com.sun.proxy.$Proxy4雇盖,cglib代理生成的類為class com.cglib.UserDao$$EnhancerByCGLIB$$552188b6忿等。
  4. 靜態(tài)代理在編譯時(shí)產(chǎn)生class字節(jié)碼文件,可以直接使用崔挖,效率高贸街。
  5. 動(dòng)態(tài)代理必須實(shí)現(xiàn)InvocationHandler接口庵寞,通過反射代理方法,比較消耗系統(tǒng)性能薛匪,但可以減少代理類的數(shù)量捐川,使用更靈活。
  6. cglib代理無需實(shí)現(xiàn)接口逸尖,通過生成類字節(jié)碼實(shí)現(xiàn)代理古沥,比反射稍快,不存在性能問題娇跟,但cglib會(huì)繼承目標(biāo)對(duì)象岩齿,需要重寫方法,所以目標(biāo)對(duì)象不能為final類苞俘。

覺得還行可以點(diǎn)個(gè)星星哦

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末盹沈,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子苗胀,更是在濱河造成了極大的恐慌襟诸,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件基协,死亡現(xiàn)場(chǎng)離奇詭異歌亲,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)澜驮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門陷揪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人杂穷,你說我怎么就攤上這事悍缠。” “怎么了耐量?”我有些...
    開封第一講書人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵飞蚓,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我廊蜒,道長(zhǎng)趴拧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任山叮,我火速辦了婚禮著榴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘屁倔。我一直安慰自己脑又,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著问麸,像睡著了一般往衷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上口叙,一...
    開封第一講書人閱讀 49,816評(píng)論 1 290
  • 那天炼绘,我揣著相機(jī)與錄音,去河邊找鬼妄田。 笑死,一個(gè)胖子當(dāng)著我的面吹牛驮捍,可吹牛的內(nèi)容都是我干的疟呐。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼东且,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼启具!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起珊泳,我...
    開封第一講書人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤鲁冯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后色查,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體薯演,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年秧了,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了跨扮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡验毡,死狀恐怖衡创,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情晶通,我是刑警寧澤璃氢,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站狮辽,受9級(jí)特大地震影響一也,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜隘竭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一塘秦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧动看,春花似錦尊剔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挨稿。三九已至,卻和暖如春京痢,著一層夾襖步出監(jiān)牢的瞬間奶甘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工祭椰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留臭家,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓方淤,卻偏偏與公主長(zhǎng)得像钉赁,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子携茂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

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