從法外狂徒張三賣房引起的代理模式

寫在之前

談到代理模式,最常用的使用場(chǎng)景莫過于 AOP 中的利用了蓝谨,在討論 AOP 的實(shí)現(xiàn)之前灌具,先來聊聊什么代理模式。

動(dòng)態(tài)代理有兩種形式譬巫,靜態(tài)代理和動(dòng)態(tài)代理咖楣,大家先不用在意兩者的概念,等了解本篇你將會(huì)發(fā)現(xiàn)其實(shí)兩者差別不大芦昔。

靜態(tài)代理

用一個(gè)簡(jiǎn)單的例子來分析什么是靜態(tài)代理诱贿,用買房張三賣房這件事兒為例,聊聊代理模式有何作用,為何如此使用如此頻繁珠十。

Subject 接口:用于對(duì)被訪問者的抽象化(比如賣房這件事兒)

SubjectImpl:被訪問者的具體實(shí)現(xiàn)(張三想要賣房)

SubjectProxy:被訪問者的代理實(shí)現(xiàn)類料扰,該類需要有一個(gè) Subject 接口具體的實(shí)例。(比如房產(chǎn)中介焙蹭,需要拿著張三授權(quán)才可以代理)

Client:訪問者的抽象晒杈。(比如李四想買房,本身是一個(gè)顧客)

代理模式

代理類本身就是替被訪問者做事的孔厉,李四想買房子拯钻,提了很多要求,比如朝南撰豺、學(xué)區(qū)房粪般;房產(chǎn)中介(SubjectProxy)看張三家的房子剛好符合李四預(yù)期,將張三的房子介紹給李四污桦;相當(dāng)于當(dāng)一個(gè)中間人的意思亩歹。有人可能會(huì)說,就類似于一個(gè)介紹的活兒?jiǎn)峁讶螅糠堑米屩薪閬韱崂υ鳎咳绻皇呛?jiǎn)單的介紹舅柜,還真不需要中介梭纹,但是中介可以幫忙跑貸款、幫忙把握合同等致份。

這樣变抽,張三只需要授權(quán)給中介,就可以一邊做別的事兒氮块,一邊更加省心地完成交易绍载。

上面的圖順理成章變成了如下的模式

賣房理解代理模式

被代理的抽象接口——房子

public interface House {
    /**
     * 賣房子
     */
    void sell();
}

被訪問的類——張三的房子

public class HouseForZhangsan implements House {


    @Override
    public void sell() {
        System.out.println("zhangsan sell house…………");
    }
}

代理類——房產(chǎn)中介

public class HouseProxy implements House {

    private House house;

    // 通過構(gòu)造方法做到每次替不同的人代理
    public HouseProxy (House house) {
        this.house = house;
    }
    @Override
    public void sell() {
        house = new HouseForZhangsan();
        house.sell();
    }
}

測(cè)試類——交易場(chǎng)所

public class Test {

    public static void main(String[] args) {
        // 構(gòu)造具體的賣房者
        House house = new HouseForZhangsan();
        // 將張三交給中介代理
        House houseForPerson = new HouseProxy(house);
        houseForPerson.sell();
    }
}

還是回到 Spring AOP 模式中,其中 SubjectProxy 就像是 SubjectImpl 的中介滔蝉,而 SubjectImpl 本身是系統(tǒng)中的 JoinPoint 所在的對(duì)象(目標(biāo)對(duì)象)击儡,順理成章地為目標(biāo)對(duì)象創(chuàng)建一個(gè)代理對(duì)象,完成切面的邏輯蝠引。

AOP 代理對(duì)象

但是各位想想阳谍,市面上并不是只有房子賣呢,張三家里有一輛空閑的車螃概,也想賣矫夯,還能去找房產(chǎn)中介嗎?

肯定不能了吊洼。

于是催生出了專門用于車交易的中介训貌,比如瓜子二手車(號(hào)稱沒有中間商賺差價(jià),哈哈哈)、二手車之家等等递沪。

<img src="https://cdn.jsdelivr.net/gh/kaoyan-guide/personPic@main/imgBlog/spring/20210417205209.png" alt="二手車平臺(tái)" style="zoom:50%;" />

再比如萬一張三突然發(fā)現(xiàn)手機(jī)用久了豺鼻,想賣掉二手手機(jī),新的 iPhone款慨,于是又出現(xiàn)了各種各樣的二手手機(jī)平臺(tái)(其實(shí)本質(zhì)也是一種中介)

<img src="https://cdn.jsdelivr.net/gh/kaoyan-guide/personPic@main/imgBlog/spring/20210417205454.png" alt="二手手機(jī)" style="zoom:40%;" />

……

……

等等拘领,那有人可能會(huì)想,能不能搞一個(gè)中介樱调,能夠啥都賣呢约素?于是更加全面的二手平臺(tái)應(yīng)運(yùn)而生了。

中介平臺(tái)

在這上面可以靈活的代理各種商品 (被代理對(duì)象)笆凌,這就達(dá)到了一種動(dòng)態(tài)中介的效果圣猎。

對(duì),沒錯(cuò)乞而,動(dòng)態(tài)代理已經(jīng)介紹完了送悔。

<img src="https://cdn.jsdelivr.net/gh/kaoyan-guide/personPic@main/imgBlog/spring/20210417210047.png" alt="image-20210417210040941" style="zoom:50%;" />

開玩笑,繼續(xù)聊聊 Spring AOP 是如何利用動(dòng)態(tài)代理的爪模。

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

可以指定接口在運(yùn)行期間動(dòng)態(tài)的生成代理對(duì)象欠啤。(換句話說:無論你要賣什么,你來的時(shí)候都可以給你找一個(gè)對(duì)應(yīng)的中介)

那么如何動(dòng)態(tài)生成代理類呢屋灌?

需要借助兩個(gè)工具洁段,一個(gè)是 java.lang.reflect.Proxy 類 和 java.lang.reflect.InvocationHandler,問題的關(guān)鍵在于如何實(shí)時(shí)的給客戶產(chǎn)生一個(gè)滿足要去的中介共郭。

這個(gè)就是借助 InvocationHandler來動(dòng)態(tài)生成代理類祠丝,還是以上面中介為例,我們姑且講要生成的代理類叫做 target.

如何動(dòng)態(tài)產(chǎn)生不同類型的中介除嘹?

第一步肯定需要知道此時(shí)替什么類型客戶代理写半,但是又不能寫得太死,我們姑且在生成代理類中先聲明一個(gè) 被代理的對(duì)象尉咕。

第二步:通過某種方式將 被代理對(duì)象通過傳入的方式傳進(jìn)來

第三步:將被代理對(duì)象與中介進(jìn)行綁定叠蝇。

/**
 * 被代理的目標(biāo)
 */
public Object target;

/**
 * 綁定委托對(duì)象,并且生成代理類
 * @param target
 * @return
 */
public Object bind(Object target) {
    this.target = target;
    //綁定該類實(shí)現(xiàn)的所有接口年缎,取得代理類
    return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                                  target.getClass().getInterfaces(), this);
}   

上述幾步部署完成之后悔捶,會(huì)明白中介要替什么人做事兒,中介做什么事兒晦款,并且將中介與客戶關(guān)聯(lián)起來炎功。

客戶與中介綁定

最后才是真正的替客戶做事兒。

public class SellInvocationHandler implements InvocationHandler {

    /**
     * 被代理的目標(biāo)
     */
    public Object target;

    /**
     * 綁定委托對(duì)象缓溅,并且生成代理類
     * @param target
     * @return
     */
    public Object bind(Object target) {
        this.target = target;
        //綁定該類實(shí)現(xiàn)的所有接口蛇损,取得代理類
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                                      target.getClass().getInterfaces(), this);
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("額外邏輯……");
        return method.invoke(target, args);
    }
}

還記得我們之前說過的嗎?

動(dòng)態(tài)代理解決的只是靈活產(chǎn)生不同代理類(換句話說靈活搭配不同類型中介)

至于做什么類型事兒,和替什么人做什么事兒這兩件事兒還是得存在淤齐。

因此仍然需要申明兩個(gè)類股囊。

做什么類型事兒

public interface House {
    /**
     * 賣房子
     */
    void sell();
}

替什么人做什么事兒

public class HouseForZhangsan implements House {
    @Override
    public void sell() {
        System.out.println("zhangsan sell house…………");
    }
}

然后就可以愉快地進(jìn)行交易了,每次有新的顧客來更啄,就可以叫不同類型的中介來服務(wù)稚疹。

public class DynamicProxyTest {

    public static void main(String[] args) {

        SellInvocationHandler invocationHandler = new SellInvocationHandler();

        // 將被訪問類和代理類相互綁定( 將房產(chǎn)中介 與 房子賣者相互綁定 )
        House house = (House) invocationHandler.bind(new HouseForZhangsan());
        
        // 真正執(zhí)行
        house.sell();
    }
}

至此,我們已經(jīng)完成了真正的靈活代理工作祭务。

動(dòng)態(tài)代理雖好内狗,卻不能解決所有的事情。比如义锥,動(dòng)態(tài)代理只能對(duì)實(shí)現(xiàn)了相應(yīng)接口 (Interface) 的類使用柳沙,如果某個(gè)類沒有實(shí)現(xiàn)任何的 Interface,就無法使用動(dòng)態(tài)代理機(jī)制為其生成相應(yīng)的動(dòng)態(tài)代理對(duì)象拌倍。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末赂鲤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子柱恤,更是在濱河造成了極大的恐慌数初,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梗顺,死亡現(xiàn)場(chǎng)離奇詭異泡孩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)荚守,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門珍德,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人搔涝,你說我怎么就攤上這事沉桌。” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵鳞上,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我几颜,道長(zhǎng)笼踩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任谷市,我火速辦了婚禮蛔垢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘迫悠。我一直安慰自己鹏漆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著艺玲,像睡著了一般括蝠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上饭聚,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天忌警,我揣著相機(jī)與錄音,去河邊找鬼秒梳。 笑死法绵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的酪碘。 我是一名探鬼主播礼烈,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼婆跑!你這毒婦竟也來了此熬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤滑进,失蹤者是張志新(化名)和其女友劉穎犀忱,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扶关,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡阴汇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了节槐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搀庶。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖铜异,靈堂內(nèi)的尸體忽然破棺而出哥倔,到底是詐尸還是另有隱情,我是刑警寧澤揍庄,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布咆蒿,位于F島的核電站,受9級(jí)特大地震影響蚂子,放射性物質(zhì)發(fā)生泄漏沃测。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一食茎、第九天 我趴在偏房一處隱蔽的房頂上張望蒂破。 院中可真熱鬧,春花似錦别渔、人聲如沸附迷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)挟秤。三九已至壹哺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間艘刚,已是汗流浹背管宵。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留攀甚,地道東北人箩朴。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像秋度,于是被迫代替她去往敵國(guó)和親炸庞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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