代理模式

現(xiàn)實生活中疯淫,存在著各種代理地来,比如海外代購,律師熙掺,游戲代練等等未斑,映射到Java中,也存在著代理币绩,匯總實際場景中的使用蜡秽,將代理分為了靜態(tài)代理和動態(tài)代理兩種模式。

StaticProxy

靜態(tài)代理拆開來缆镣,靜態(tài)指的是在程序運行前就已經(jīng)存在了代理類的字節(jié)碼文件芽突,也就是這個代理類是程序員手動編寫的,代理類和被代理類的關(guān)系就已經(jīng)確定了的董瞻,代理指的就是編寫的代理類對被代理類的增強效果寞蚌。

場景一:

張三看上了海外一家公司CompanyA制造的一款包田巴,如果他自己想要買這款包,他就需要出國購買挟秤,出國前辦理簽證壹哺,坐飛機,買包艘刚,坐飛機管宵,回家。但他并不像這么麻煩攀甚,于是他便找了一家代購店箩朴,于是張三所做的事情就是給錢,拿包完事云稚,至于中間代購店如何出的國隧饼,如何坐的飛機,跟張三毫無關(guān)系静陈,因為張三的目的就是要這個包燕雁,抽象如下:

業(yè)務(wù)接口BusinessA(制造包)

/**
 * Description: 制造包的業(yè)務(wù)接口
 *
 * @Author 70KG
 * @Date 2019/3/12
 * @Since v1.0
 */
public interface BusinessA {

    void createBag();

}

生產(chǎn)包的廠商CompanyA:

/**
 * Description: 國外生產(chǎn)包的廠商A,但不限于A鲸拥,可能還有B拐格,C 。刑赶。捏浊。
 *
 * @Author 70KG
 * @Date 2019/3/12
 * @Since v1.0
 */
public class CompanyA implements BusinessA {

    @Override
    public void createBag() {
        System.out.println("CompanyA公司生產(chǎn)的包。");
    }

}

代理類ProxyStore:

/**
 * Description: 代理商店撞叨,它對外宣稱有賣包的業(yè)務(wù)
 *
 * @Author 70KG
 * @Date 2019/3/12
 * @Since v1.0
 */
public class ProxyStore implements BusinessA {

    private CompanyA companyA;

    @Override
    public void createBag() {
        // 1. 在購買前做的事
        doBefore();
        // 2. 從廠商購買
        companyA.createBag();
        // 3. 運回國內(nèi)
        doAfter();
    }

    private void doBefore() {
        System.out.println("辦理簽證金踪,坐飛機去國外。");
    }

    private void doAfter() {
        System.out.println("從國外運回國內(nèi)牵敷。");
    }

    public CompanyA getCompanyA() {
        return companyA;
    }

    public void setCompanyA(CompanyA companyA) {
        this.companyA = companyA;
    }
}

測試:

/**
 * Description:
 *
 * @Author 70KG
 * @Date 2019/3/12
 * @Since v1.0
 */
public class Test01 {

    @Test
    public void m1() {
        // 創(chuàng)建代理類
        ProxyStore proxyStore = new ProxyStore();
        // 讓代理有買包的權(quán)利
        proxyStore.setCompanyA(new CompanyA());
        // 由代理去購買
        proxyStore.createBag();
    }
}

靜態(tài)代理的優(yōu)缺點:

  • 優(yōu)點:業(yè)務(wù)類只需要關(guān)注業(yè)務(wù)本身胡岔,無需關(guān)心業(yè)務(wù)前后所做的增強處理,這些都交給代理來處理枷餐。
  • 缺點:代理類代理的業(yè)務(wù)單一靶瘸,假如業(yè)務(wù)接口擴展了,還需要改動代理類的代碼毛肋,增加維護的成本怨咪。

DynamicProxy

動態(tài)代理拆開,動態(tài)指的是代理類的源碼是在程序運行期間由JVM根據(jù)反射等機制動態(tài)的生成润匙,所以不存在代理類的字節(jié)碼文件诗眨。代理類和委托類的關(guān)系是在程序運行時確定。

  • Proxy類孕讳,它是所有動態(tài)代理的父類辽话,其中利用靜態(tài)方法newProxyInstance()來創(chuàng)建代理類肄鸽。
  • InvocationHandler接口,其中invoke方法的作用就是在代理類中調(diào)用目標方法油啤,并返回結(jié)果典徘。
  • invoke方法的三個參數(shù)
    • proxy:代理對象的實例
    • method:通過它可以調(diào)用真實方法
    • args:傳過來的參數(shù)

還是上面靜態(tài)代理的例子(以下為JDK動態(tài)代理,只能代理接口):

業(yè)務(wù)接口BusinessA:

/**
 * Description: 制造包的業(yè)務(wù)接口
 *
 * @Author 70KG
 * @Date 2019/3/12
 * @Since v1.0
 */
public interface BusinessA {

    void createBag();

}

業(yè)務(wù)接口BusinessB:

/**
 * Description: 制造手表的業(yè)務(wù)接口
 *
 * @Author 70KG
 * @Date 2019/3/12
 * @Since v1.0
 */
public interface BusinessB {

    void createWatch();

}

生產(chǎn)包的廠商CompanyA:

/**
 * Description: 國外生產(chǎn)包的廠商A益咬,但不限于A逮诲,可能還有B,C 幽告。梅鹦。。
 *
 * @Author 70KG
 * @Date 2019/3/12
 * @Since v1.0
 */
public class CompanyA implements BusinessA {

    @Override
    public void createBag() {
        System.out.println("CompanyA公司生產(chǎn)的包冗锁。");
    }

}

生產(chǎn)手表的廠商CompanyB:

/**
 * Description: 國外生產(chǎn)手表的廠商CompanyB
 *
 * @Author 70KG
 * @Date 2019/3/12
 * @Since v1.0
 */
public class CompanyB implements BusinessB {

    @Override
    public void createWatch() {
        System.out.println("CompanyB公司生產(chǎn)的手表齐唆。");
    }

}

代理類:

需要實現(xiàn)InvocationHandler接口,并且重寫invoke方法冻河,還需要一個獲取代理對象的方法getProxyInstance箍邮。

/**
 * description 實現(xiàn)InvocationHandler接口的動態(tài)代理類
 *
 * @author 70KG
 * @date 2019/3/12
 */
public class ProxyClassBeans implements InvocationHandler {

    /**
     * 被代理的對象
     **/
    private Object tar;

    /**
     * 利用反射來調(diào)用目標方法
     **/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1. 調(diào)用目標方法之前
        doBefore();
        // 2. 調(diào)用目標方法
        Object invoke = method.invoke(tar, args);
        // 3. 調(diào)用目標方法之后
        doAfter();
        return invoke;
    }

    /** 綁定該類實現(xiàn)的所有接口,取得代理類 **/
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(tar.getClass().getClassLoader(), tar.getClass().getInterfaces(), this);
    }

    // ===================動態(tài)代理類中的增強方法========================

    public void doBefore() {
        System.out.println("在國外尋找優(yōu)質(zhì)的貨源叨叙。");
    }

    public void doAfter() {
        System.out.println("精包裝安全郵寄回國內(nèi)锭弊。");
    }

    // ===========================get/set=============================


    public Object getTar() {
        return tar;
    }

    public void setTar(Object tar) {
        this.tar = tar;
    }
}

測試:

/**
 * description
 *
 * @author 70KG
 * @date 2019/3/12
 */
public class MainTest {
    public static void main(String[] args) {
        // 獲取動態(tài)代理類
        ProxyClassBeans proxyClassBeans = new ProxyClassBeans();
        IBusinessA companyA = new CompanyA();
        // 將目標類交給代理類
        proxyClassBeans.setTar(companyA);
        // 拿到代理對象
        IBusinessA instance1 = (IBusinessA) proxyClassBeans.getProxyInstance();
        instance1.createBag();

        System.out.println("=================================");

        IBusinessB companyB = new CompanyB();
        proxyClassBeans.setTar(companyB);
        IBusinessB instance2 = (IBusinessB) proxyClassBeans.getProxyInstance();
        instance2.saleWatch();
    }
}

運行結(jié)果:

在國外尋找優(yōu)質(zhì)的貨源。
CompanyA公司生產(chǎn)的包擂错。
精包裝安全郵寄回國內(nèi)味滞。
=================================
在國外尋找優(yōu)質(zhì)的貨源。
CompanyB公司生產(chǎn)的手表钮呀。
精包裝安全郵寄回國內(nèi)剑鞍。

由運行結(jié)果可知,動態(tài)代理在增添業(yè)務(wù)的時候(增添了賣手表的業(yè)務(wù))爽醋,只需要調(diào)用者傳入不同的接口就好了攒暇,代理類不需要改動。

動態(tài)代理的優(yōu)缺點:

  • 優(yōu)點:同樣子房,我們只關(guān)注業(yè)務(wù)類的邏輯即可,并且如果新增業(yè)務(wù)類就轧,代理類的代碼根本不需要動证杭,因為代理對象是根據(jù)傳入的接口類型生成的,方便擴展妒御。
  • 缺點:JDK動態(tài)代理解愤,在生成代理對象的時候必須傳入業(yè)務(wù)類的接口類型,也就是只能夠代理接口乎莉。能夠代理類的動態(tài)代理是CGLIB動態(tài)代理送讲。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末奸笤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子哼鬓,更是在濱河造成了極大的恐慌监右,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件异希,死亡現(xiàn)場離奇詭異健盒,居然都是意外死亡,警方通過查閱死者的電腦和手機称簿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門扣癣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人憨降,你說我怎么就攤上這事父虑。” “怎么了授药?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵士嚎,是天一觀的道長。 經(jīng)常有香客問我烁焙,道長航邢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任骄蝇,我火速辦了婚禮膳殷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘九火。我一直安慰自己赚窃,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布岔激。 她就那樣靜靜地躺著勒极,像睡著了一般。 火紅的嫁衣襯著肌膚如雪虑鼎。 梳的紋絲不亂的頭發(fā)上辱匿,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天,我揣著相機與錄音炫彩,去河邊找鬼匾七。 笑死,一個胖子當(dāng)著我的面吹牛江兢,可吹牛的內(nèi)容都是我干的昨忆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼杉允,長吁一口氣:“原來是場噩夢啊……” “哼邑贴!你這毒婦竟也來了席里?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤拢驾,失蹤者是張志新(化名)和其女友劉穎奖磁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體独旷,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡署穗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了嵌洼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片案疲。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖麻养,靈堂內(nèi)的尸體忽然破棺而出褐啡,到底是詐尸還是另有隱情,我是刑警寧澤鳖昌,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布备畦,位于F島的核電站,受9級特大地震影響许昨,放射性物質(zhì)發(fā)生泄漏懂盐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一糕档、第九天 我趴在偏房一處隱蔽的房頂上張望莉恼。 院中可真熱鬧,春花似錦速那、人聲如沸俐银。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捶惜。三九已至,卻和暖如春荔烧,著一層夾襖步出監(jiān)牢的瞬間吱七,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工鹤竭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留踊餐,地道東北人。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓诺擅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親啡直。 傳聞我的和親對象是個殘疾皇子烁涌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,652評論 2 354