Java 實(shí)現(xiàn)代理模式(代碼篇)

總覽

這篇文章我們簡(jiǎn)單講一講Java語(yǔ)言中代理模式的幾種實(shí)現(xiàn)方式。這篇文章僅僅是在代碼上的實(shí)現(xiàn)。原理篇會(huì)在后面分開(kāi)來(lái)闡述宿百。

什么是代理模式

要用Java實(shí)現(xiàn)代理工三,首先要知道什么是代理。
下面是在百度百科上關(guān)于代理模式的定義:

為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)兑巾。在某些情況下,一個(gè)對(duì)象不適合或者不能直接引用另一個(gè)對(duì)象,而代理對(duì)象可以在客戶端和目標(biāo)對(duì)象之間起到中介的作用坠宴。

怎么使用代理模式

具體在Java里的實(shí)現(xiàn),我們?cè)谙旅嫒齻€(gè)模塊代碼中來(lái)闡述绷旗。這里就簡(jiǎn)單介紹一下一個(gè)代理模式的組成喜鼓,它在一定程度上表現(xiàn)了代理模式的基本使用副砍。

抽象角色:通過(guò)接口或抽象類聲明真實(shí)角色實(shí)現(xiàn)的業(yè)務(wù)方法。
代理角色:實(shí)現(xiàn)抽象角色庄岖,是真實(shí)角色的代理豁翎,通過(guò)真實(shí)角色的業(yè)務(wù)邏輯方法來(lái)實(shí)現(xiàn)抽象方法,并可以附加自己的操作隅忿。
真實(shí)角色:實(shí)現(xiàn)抽象角色谨垃,定義真實(shí)角色所要實(shí)現(xiàn)的業(yè)務(wù)邏輯,供代理角色調(diào)用硼控。

使用代理模式的好處

  1. 職責(zé)清晰
    真實(shí)的角色就是實(shí)現(xiàn)實(shí)際的業(yè)務(wù)邏輯刘陶,不用關(guān)心其他非本職責(zé)的事務(wù),通過(guò)后期的代理完成一件完成事務(wù)牢撼,附帶的結(jié)果就是編程簡(jiǎn)潔清晰匙隔。
  2. 代理對(duì)象可以在客戶端和目標(biāo)對(duì)象之間起到中介的作用,這樣起到了中介的作用和保護(hù)了目標(biāo)對(duì)象的作用熏版。
  3. 高擴(kuò)展性
  4. 降低了耦合

在代理類中纷责,我們可以添加自己的動(dòng)作,不在真實(shí)角色業(yè)務(wù)邏輯中的操作我們可以在代理類中進(jìn)行添加撼短。保證了業(yè)務(wù)邏輯的正確性再膳。用一個(gè)爛大街的例子。現(xiàn)在我們有四個(gè)角色曲横,一個(gè)是房東喂柒,一個(gè)是房子,一個(gè)是租客禾嫉,一個(gè)是中介灾杰。處理租客,其他三個(gè)對(duì)應(yīng)的代理模式中的角色想必非常清楚熙参。如果租客和房東直接溝通艳吠,也就是看房->付定金->入住這樣一個(gè)邏輯,但是看房和入住跟房東都沒(méi)有太大的直接關(guān)系孽椰。這個(gè)時(shí)候昭娩,如果看房、入住都讓房東親自來(lái)處理黍匾,那我們的房東必然會(huì)忙的不可開(kāi)交栏渺,并且不利于我們可愛(ài)的房東從復(fù)雜的租房活動(dòng)中清閑起來(lái),這個(gè)時(shí)候膀捷,我們使用代理模式迈嘹,也就是說(shuō)房東找一個(gè)中介公司削彬,把自己提供租房這個(gè)服務(wù)交給中介來(lái)代理全庸,讓他們完成租房的工作秀仲。我們的房東只要負(fù)責(zé)收錢就好,看房啊壶笼、入住辦理啊神僵、這種事情就交給中介(代理)去處理就好。這樣覆劈,我們實(shí)現(xiàn)了保礼,職責(zé)清晰、保護(hù)了目標(biāo)對(duì)象责语、高擴(kuò)展度炮障、降低了耦合的優(yōu)良形態(tài)

靜態(tài)代理

靜態(tài)代理的實(shí)現(xiàn)

到這里已經(jīng)讀了挺多字了坤候。這里我們上幾段代碼來(lái)簡(jiǎn)單講Java中靜態(tài)代理的實(shí)現(xiàn)胁赢。

首先是一個(gè)接口。也就是所謂的接口角色白筹。我們通過(guò)接口角色聲明真實(shí)角色的業(yè)務(wù)智末。

public interface Hello {
    void say(String name);
}

然后是一個(gè)實(shí)現(xiàn)類。也就是所謂的真實(shí)角色徒河,在這個(gè)類中系馆,我們定義所要實(shí)現(xiàn)的業(yè)務(wù)邏輯。

public class HelloImp implements Hello{

    @Override
    public void say(String name) {
        System.out.println("Hello! " + name);
    }
}

最后是一個(gè)代理類顽照。我們使用代理類來(lái)代理真實(shí)類由蘑,通過(guò)真實(shí)類對(duì)應(yīng)方法中的業(yè)務(wù)邏輯來(lái)實(shí)現(xiàn)接口中的抽象方法。特別的是代兵,我們可以在代理類中添加自己的操作纵穿。

動(dòng)態(tài)代理

靜態(tài)代理未免有點(diǎn)太死板了、這么直接固定的代理方式奢人,會(huì)導(dǎo)致我們每有一個(gè)新的Hello接口的實(shí)現(xiàn)谓媒,就需要編寫(xiě)一個(gè)新的代理類。這未免也有點(diǎn)太臃腫了何乎。Java待我們很好句惯,給我提供了兩種動(dòng)態(tài)代理的實(shí)現(xiàn)方式,一種是JDK動(dòng)態(tài)代理支救,一種是CGLib動(dòng)態(tài)代理抢野。JDK動(dòng)態(tài)代理是Java原生的,而CGLib則是一個(gè)開(kāi)源項(xiàng)目各墨。(點(diǎn)擊下載CGLib jar包)

jdk動(dòng)態(tài)代理

我們首先來(lái)講Jdk動(dòng)態(tài)代理的實(shí)現(xiàn)指孤,然后通過(guò)和CGLib的不同引入CGLib動(dòng)態(tài)代理。老套路,直接粘貼代碼恃轩。

動(dòng)態(tài)代理代碼:

package com.blaze.study.aop.dynamic;

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

public class DynamicProxyDemo06 implements InvocationHandler{
    private Object target;
    
    public DynamicProxyDemo06(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        operator1();
        Object result = method.invoke(target,args);
        operator2();
        
        return result;
    }
    
    public void operator1(){
        System.out.println("添加的操作1");
    }
    
    public void operator2(){
        System.out.println("添加的操作2");
    }
}

這里我們通過(guò)實(shí)現(xiàn)InvocationHandler接口结洼,并且提供其invoke方法的實(shí)現(xiàn),就可以為所欲為叉跛,動(dòng)態(tài)代理任何我們想要代理的類松忍。

接下面,通過(guò)一段使用者來(lái)闡述一些動(dòng)態(tài)代理代碼上的優(yōu)化筷厘。

package com.blaze.study.aop.dynamic;

import com.blaze.study.aop.proxy.Hello;
import com.blaze.study.aop.proxy.HelloImp;

import java.lang.reflect.Proxy;

public class DynamicProxyTestDemo06 {
    public static void main(String[] args) {
        Hello h = new HelloImp();
        DynamicProxyDemo06 dy = new DynamicProxyDemo06(h);
        Hello hello = (Hello)Proxy.newProxyInstance(h.getClass().getClassLoader(),
                h.getClass().getInterfaces(),
                dy);

        hello.say("Sherlock");
    }
}

我們通過(guò)Proxy.newProxyInstance來(lái)生成Hello的引用并且使用通過(guò)類型轉(zhuǎn)換最后來(lái)調(diào)用say方法鸣峭。

根據(jù)在使用層面代碼越簡(jiǎn)單越好的原則,我們通過(guò)可以通過(guò)在DynamicProxyDemo06類中添加這樣的方法來(lái)改造我們的動(dòng)態(tài)代理類酥艳。

@SuppressWarnings("unchecked")
public <T> T getProxy() {
    return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            this);
}

我們通過(guò)范型來(lái)完成類型的匹配摊溶,而不需要在調(diào)用者中使用強(qiáng)制方式的來(lái)進(jìn)行類型轉(zhuǎn)換。

這個(gè)時(shí)候充石,我們就可以在調(diào)用者中通過(guò)下面的簡(jiǎn)單代碼來(lái)調(diào)用被調(diào)用的方法更扁。

package com.blaze.study.aop.dynamic;

import com.blaze.study.aop.proxy.Hello;
import com.blaze.study.aop.proxy.HelloImp;

public class DynamicProxyTestDemo06 {
    public static void main(String[] args) {
        Hello h = new DynamicProxyDemo06(new HelloImp()).getProxy();
        h.say("Sherlock");
    }
}

動(dòng)態(tài)代理跟靜態(tài)代理的區(qū)別就是在動(dòng)態(tài)代理中我們不需要為每一個(gè)類寫(xiě)一個(gè)代理類。我們僅僅通過(guò)DynamicProxyDemo06這一個(gè)類來(lái)代理我們想要代理的任何類赫冬。比如我們有一個(gè)World接口浓镜、和World實(shí)現(xiàn)類。

World.java

package com.blaze.study.aop.proxy;

public interface World {
    void say(String word);
}

WorldImpl.java

package com.blaze.study.aop.proxy;

public class WorldImpl implements World{

    @Override
    public void say(String word) {
        System.out.println(word + "\tWorld!");
    }
}

這樣我們通過(guò)代碼調(diào)用代理的服務(wù)劲厌,并且添加我們的操作膛薛,簡(jiǎn)單輕松。

package com.blaze.study.aop.dynamic;

import com.blaze.study.aop.proxy.Hello;
import com.blaze.study.aop.proxy.HelloImp;
import com.blaze.study.aop.proxy.World;
import com.blaze.study.aop.proxy.WorldImpl;

public class DynamicProxyTestDemo06 {
    public static void main(String[] args) {
        Hello h = new DynamicProxyDemo06(new HelloImp()).getProxy();
        h.say("Sherlock");

/**新的被代理類*/
        World w = new DynamicProxyDemo06(new WorldImpl()).getProxy();
        w.say("Blaze");
    }
}

CGLib動(dòng)態(tài)代理

接下面要講的動(dòng)態(tài)代理實(shí)現(xiàn)的方式就是CGLib补鼻,CGLib博大精深哄啄,這篇文章里我們就完成一個(gè)簡(jiǎn)單的實(shí)現(xiàn)。JDK動(dòng)態(tài)代理必須要我們的被代理類有一個(gè)接口风范,如果沒(méi)有接口咨跌,就會(huì)報(bào)類型轉(zhuǎn)化錯(cuò)誤。

報(bào)錯(cuò)信息

我們僅簡(jiǎn)單的刪除掉了WorldImpl類實(shí)現(xiàn)的接口硼婿。就爆出這樣嚴(yán)重的錯(cuò)誤锌半。假如我們不想要實(shí)現(xiàn)任何接口呢,不想用接口來(lái)描述所要代理的方法寇漫。

這個(gè)時(shí)候刊殉,我們最好使用CGLib動(dòng)態(tài)代理,它可以代理沒(méi)有任何接口的類州胳。接下來(lái)我們就來(lái)寫(xiě)這樣的一個(gè)動(dòng)態(tài)代理记焊。

代理類 --> CGLibProxyDemo05

package com.blaze.study.aop.cglib;

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

import java.lang.reflect.Method;

public class CGLibProxyDemo05 implements MethodInterceptor{

    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<?> cls){
        return (T) Enhancer.create(cls,this);
    }

    @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy)throws Throwable{
        operator1();
        Object result = proxy.invokeSuper(target,args);
        operator2();

        return result;
    }

    public void operator1(){
        System.out.println("Operator1");
    }

    public void operator2(){
        System.out.println("Operator2");
    }
}

我們通過(guò)提供的接口,調(diào)用invokeSuper方法栓撞,來(lái)完成CGLib的動(dòng)態(tài)代理遍膜。我們通過(guò)這樣的方式來(lái)調(diào)用服務(wù)碗硬。

使用者 --> CGLibProxyTestDemo05

package com.blaze.study.aop.cglib;

import com.blaze.study.aop.proxy.Hello;
import com.blaze.study.aop.proxy.HelloImp;
import com.blaze.study.aop.proxy.WorldImpl;

public class CGLibProxyTestDemo05 {
    public static void main(String[] args) {
        Hello h = new CGLibProxyDemo05().getProxy(HelloImp.class);
        h.say("Sherlock");

        WorldImpl w = new CGLibProxyDemo05().getProxy(WorldImpl.class);
        w.say("Blaze");
    }
}

結(jié)果

程序執(zhí)行結(jié)果

在上面的代碼中,我們已WorldImpl實(shí)現(xiàn)接口的聲明刪除掉了瓢颅。通過(guò)程序執(zhí)行的結(jié)果恩尾,我們可以清楚的看到CGLib動(dòng)態(tài)代理不僅可以代理動(dòng)態(tài)的有接口的HelloImp類,也可以代理沒(méi)有實(shí)現(xiàn)任何接口的WorldImpl類惜索。這是比JDk動(dòng)態(tài)代理進(jìn)步的地方。而關(guān)于這一塊的原理剃浇,我們也會(huì)用相應(yīng)的幾篇博客一起探索其中的原理巾兆。Flag立在這里。

總結(jié)

通過(guò)上面文字的編寫(xiě)虎囚,我已經(jīng)沒(méi)有力氣寫(xiě)總結(jié)了角塑。所以大家自己總結(jié)吧。謝謝大家的支持淘讥。歡迎訪問(wèn)我的個(gè)人博客圃伶。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蒲列,隨后出現(xiàn)的幾起案子窒朋,更是在濱河造成了極大的恐慌,老刑警劉巖蝗岖,帶你破解...
    沈念sama閱讀 212,185評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侥猩,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡抵赢,警方通過(guò)查閱死者的電腦和手機(jī)欺劳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,445評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)铅鲤,“玉大人划提,你說(shuō)我怎么就攤上這事⌒舷恚” “怎么了鹏往?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,684評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)骇塘。 經(jīng)常有香客問(wèn)我掸犬,道長(zhǎng),這世上最難降的妖魔是什么绪爸? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,564評(píng)論 1 284
  • 正文 為了忘掉前任湾碎,我火速辦了婚禮,結(jié)果婚禮上奠货,老公的妹妹穿的比我還像新娘介褥。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,681評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布柔滔。 她就那樣靜靜地躺著溢陪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪睛廊。 梳的紋絲不亂的頭發(fā)上形真,一...
    開(kāi)封第一講書(shū)人閱讀 49,874評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音超全,去河邊找鬼咆霜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛嘶朱,可吹牛的內(nèi)容都是我干的蛾坯。 我是一名探鬼主播,決...
    沈念sama閱讀 39,025評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼疏遏,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼脉课!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起财异,我...
    開(kāi)封第一講書(shū)人閱讀 37,761評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤倘零,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后戳寸,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體视事,經(jīng)...
    沈念sama閱讀 44,217評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,545評(píng)論 2 327
  • 正文 我和宋清朗相戀三年庆揩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了俐东。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,694評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡订晌,死狀恐怖虏辫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情锈拨,我是刑警寧澤砌庄,帶...
    沈念sama閱讀 34,351評(píng)論 4 332
  • 正文 年R本政府宣布,位于F島的核電站奕枢,受9級(jí)特大地震影響娄昆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缝彬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,988評(píng)論 3 315
  • 文/蒙蒙 一萌焰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧谷浅,春花似錦扒俯、人聲如沸奶卓。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,778評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)夺姑。三九已至,卻和暖如春掌猛,著一層夾襖步出監(jiān)牢的瞬間盏浙,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,007評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工荔茬, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留废膘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,427評(píng)論 2 360
  • 正文 我出身青樓兔院,卻偏偏與公主長(zhǎng)得像殖卑,于是被迫代替她去往敵國(guó)和親站削。 傳聞我的和親對(duì)象是個(gè)殘疾皇子坊萝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,580評(píng)論 2 349

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

  • 代理模式是常用的結(jié)構(gòu)型設(shè)計(jì)模式之一,當(dāng)無(wú)法直接訪問(wèn)某個(gè)對(duì)象或訪問(wèn)某個(gè)對(duì)象存在困難時(shí)可以通過(guò)一個(gè)代理對(duì)象來(lái)間接訪問(wèn)许起,...
    SnDragon閱讀 1,295評(píng)論 2 4
  • 什么是代理模式 代理模式是對(duì)象的結(jié)構(gòu)模式十偶。代理模式為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。簡(jiǎn)單來(lái)說(shuō)园细,在某些情...
    walidake閱讀 3,213評(píng)論 4 27
  • Java代理和動(dòng)態(tài)代理機(jī)制分析和應(yīng)用 概述 代理是一種常用的設(shè)計(jì)模式惦积,其目的就是為其他對(duì)象提供一個(gè)代理以控制對(duì)某個(gè)...
    丸_子閱讀 3,016評(píng)論 6 57
  • 原文連接 簡(jiǎn)介 Java編程的目標(biāo)是實(shí)現(xiàn)現(xiàn)實(shí)不能完成的,優(yōu)化現(xiàn)實(shí)能夠完成的猛频,是一種虛擬技術(shù)狮崩。生活中的方方面面都可以...
    zjk_00閱讀 341評(píng)論 0 0
  • 為期一周的學(xué)習(xí)終于結(jié)束了,這一次大開(kāi)眼界鹿寻,見(jiàn)到了自己期待已久的課程睦柴,也是夢(mèng)寐以求的,自己以前也這么設(shè)想過(guò)體育課可以...
    愛(ài)游泳的Danny閱讀 246評(píng)論 0 2