設(shè)計(jì)模式 - 代理模式

前言:

在開(kāi)發(fā)中一定被老大這樣說(shuō)過(guò)不要隨意去修改別人已經(jīng)寫(xiě)好的代碼或者方法。但我們又希望原來(lái)的對(duì)象可以得到擴(kuò)展扇雕,又能保護(hù)原對(duì)象,這時(shí)代理模式就出來(lái)了。
在百科中這樣描述(定義):為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)地熄。在某些情況下,一個(gè)對(duì)象不適合或者不能直接引用另一個(gè)對(duì)象芯杀,而代理對(duì)象可以在客戶端和目標(biāo)對(duì)象之間起到中介的作用端考。aop的實(shí)現(xiàn)對(duì)方法前后進(jìn)行攔截雅潭,改變?cè)袑?duì)象或方法都是基于代理模式。代理模式分為靜態(tài)代理和jdk動(dòng)態(tài)代理却特。

靜態(tài)代理

需求:張三看上了妹子扶供,怕被拒絕而苦惱,于是李四說(shuō)我替你把她約出來(lái)裂明。

  1. 定義代理和被代理的公共的接口
//張三和李四的公共行為
public interface Person {
    void meetGirl();
}

  1. 實(shí)現(xiàn)person接口,即具體動(dòng)作的實(shí)現(xiàn)
//動(dòng)作的具體實(shí)現(xiàn)
public class Man implements Person{

    private String name;

    public Man(String name){
        this.name = name;
    }

    @Override
    public void meetGirl() {
        System.out.println(name+"正在和女孩吃飯椿浓,感謝你李四");
    }
}
  1. 持有一個(gè)被代理對(duì)象的代理類,同樣要實(shí)現(xiàn)person接口
//man代理類
public class ManProxy implements Person {

    //被代理的對(duì)象
    private Man man;

    //代理
    public ManProxy(Person man){
          this.man = (Man) man;
    }
    //被代理的執(zhí)行接口方法
    @Override
    public void meetGirl() {
        man.meetGirl();
    }
}
  1. client 測(cè)試闽晦,李四替張三約妹子
//代理的測(cè)試類
public class ProxyClient {

    @Test
    public void test(){
        //被代理的對(duì)象
        Person zhangsan = new Man("張三");
        //生成代理對(duì)象李四 幫張三去約姑娘扳碍,把張三傳給代理對(duì)象
        Person lisi = new ManProxy(zhangsan);
        lisi.meetGirl();
    }
}
image.png

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

代理類在程序運(yùn)行時(shí)創(chuàng)建的代理方式被成為動(dòng)態(tài)代理(生成相應(yīng)的class文件在加入jvm)。 我們上面靜態(tài)代理的例子中仙蛉,代理類(ManProxy實(shí)現(xiàn)了接口和業(yè)務(wù)調(diào)用)是自己定義好的笋敞,在程序運(yùn)行之前就已經(jīng)編譯完成。然而動(dòng)態(tài)代理荠瘪,代理類并不是在代碼中定義的夯巷,而是在運(yùn)行時(shí)根據(jù)我們?cè)诖a中的“指示”動(dòng)態(tài)生成的, 動(dòng)態(tài)代理的優(yōu)勢(shì)在于可以很方便的對(duì)代理類的函數(shù)進(jìn)行統(tǒng)一的處理巧还,而不用修改每個(gè)代理類中的方法鞭莽。動(dòng)態(tài)代理分為兩種,一種分為基于接口的jdk代理麸祷;另一種則是基于類的代理如cglib(spring aop 實(shí)現(xiàn)),javassist等澎怒。

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

JVM根據(jù)傳進(jìn)來(lái)的 業(yè)務(wù)實(shí)現(xiàn)類對(duì)象 以及 方法名 ,動(dòng)態(tài)地創(chuàng)建了一個(gè)代理類的class文件并被字節(jié)碼引擎執(zhí)行阶牍,然后通過(guò)該代理類對(duì)象進(jìn)行方法調(diào)用喷面。我們需要做的,只需指定代理類的預(yù)處理走孽、調(diào)用后操作惧辈;在java的java.lang.reflect包下提供了一個(gè)Proxy類和一個(gè)InvocationHandler接口,實(shí)現(xiàn)InvocationHandler接口就可以生成被代理的對(duì)象磕瓷。

  1. 假如如以上person 接口不變
  2. 業(yè)務(wù)邏輯的具體實(shí)現(xiàn)
public class RealPerson implements Person {
    @Override
    public void meetGirl() {
        System.out.println("他們說(shuō)你最美盒齿,來(lái)自jdk動(dòng)態(tài)代理");
    }
}

3 . 實(shí)現(xiàn)InvocationHandler接口定義代理類

//實(shí)現(xiàn)invocationHandler 接口的jdk動(dòng)態(tài)代理類
public class SubjectHandler implements InvocationHandler {

    //被代理的對(duì)象,包含相應(yīng)的業(yè)務(wù)方法
    private Object target;
    //綁定被代理的對(duì)象
    public SubjectHandler(Object target){
        this.target = target;
    }
//    //或者如下調(diào)用是會(huì)更加簡(jiǎn)單 直接new SubjectHandler.bind(targect)
//    public Object bind(Object target) {
//        this.target = target;  //接收業(yè)務(wù)實(shí)現(xiàn)類對(duì)象
//
//        //通過(guò)反射機(jī)制,創(chuàng)建一個(gè)代理類對(duì)象實(shí)例并返回困食。用戶進(jìn)行方法調(diào)用時(shí)使用
//        //創(chuàng)建代理對(duì)象時(shí)边翁,需要傳遞該業(yè)務(wù)類的類加載器、接口硕盹、handler實(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 {
        //具體要執(zhí)行的邏輯
        Object result = null;
        System.out.println("代理前的操作-----");
        //合并邏輯符匾,并通過(guò)return決定是否執(zhí)行
        result = method.invoke(target,args);
        System.out.println("方法處理后的操作-----");
        //執(zhí)行后續(xù)操作
        return result;
    }
}
  1. 測(cè)試
public class JdkProxyClient {

    @Test
    public void test(){
        //需要代理的目標(biāo)對(duì)象
        RealPerson realPerson = new RealPerson();
        //攔截器
        SubjectHandler interceptor = new SubjectHandler(realPerson);
        //生成代理類對(duì)象,執(zhí)行代理類的業(yè)務(wù)方法
        //newProxyInstance 參數(shù):1.目標(biāo)類的加載器 2.目標(biāo)類接口 3.攔截器(處理的類)
        Person person = (Person) Proxy.newProxyInstance(realPerson.getClass().getClassLoader(),realPerson.getClass().getInterfaces(),interceptor);
        //執(zhí)行代理的業(yè)務(wù)方法
        person.meetGirl();
    }
}
spring 中的cglib 動(dòng)態(tài)代理

JDK動(dòng)態(tài)代理的代理對(duì)象在創(chuàng)建時(shí)瘩例,需要根據(jù)接口內(nèi)的方法名進(jìn)行調(diào)用啊胶,如果業(yè)務(wù)實(shí)現(xiàn)類是沒(méi)有實(shí)現(xiàn)接口或者業(yè)務(wù)實(shí)現(xiàn)類中新增了接口中沒(méi)有的方法甸各,就無(wú)法使用JDK動(dòng)態(tài)代理了(因?yàn)闊o(wú)法被調(diào)用),spirng 的cglib是針對(duì)實(shí)現(xiàn)代理的需要實(shí)現(xiàn)MethodInterceptor 接口

  1. 假設(shè)以上實(shí)現(xiàn)類不變,接口可有可無(wú)焰坪,則定義代理類如下
//基于spring 的cglib的動(dòng)態(tài)代理
public class CglibInterceptor implements MethodInterceptor {
    //代理對(duì)象
    private Object target;
    //綁定被代理的對(duì)象
    public Object createProxyObject(Object obj) {
        this.target = obj;
        //創(chuàng)建代理類
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(this);
        Object proxyObj = enhancer.create();
        return proxyObj;// 返回代理對(duì)象
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object result = null;
        //執(zhí)行業(yè)務(wù)邏輯
        System.out.println("我是cglib代理");
        //合并邏輯
        result = method.invoke(target,objects);
        return result;  //返回結(jié)果
    }
}
  1. 測(cè)試
public class CglibProxyClient {
    @Test
    public void test(){
        //目標(biāo)對(duì)象
        RealPerson realPerson = new RealPerson();
        //攔截器
        CglibInterceptor cglibInterceptor = new CglibInterceptor();
        //獲得代理對(duì)象,區(qū)別jdk動(dòng)態(tài)趣倾,不需要接口
        RealPerson cglib = (RealPerson) cglibInterceptor.createProxyObject(realPerson);
        cglib.meetGirl();

    }
}
image.png

總結(jié)

1.JDK動(dòng)態(tài)代理只能對(duì)實(shí)現(xiàn)了接口的類生成代理,通過(guò)Proxy.newProxyInstance產(chǎn)生代理對(duì)象而不能針對(duì)類琳彩。
2.CGLIB是針對(duì)類實(shí)現(xiàn)代理誊酌,主要是對(duì)指定的類生成一個(gè)子類部凑,覆蓋其中的方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末露乏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子涂邀,更是在濱河造成了極大的恐慌瘟仿,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件比勉,死亡現(xiàn)場(chǎng)離奇詭異劳较,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)浩聋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)观蜗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人衣洁,你說(shuō)我怎么就攤上這事墓捻。” “怎么了坊夫?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵砖第,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我环凿,道長(zhǎng)梧兼,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任智听,我火速辦了婚禮羽杰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘到推。我一直安慰自己考赛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布环肘。 她就那樣靜靜地躺著欲虚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悔雹。 梳的紋絲不亂的頭發(fā)上复哆,一...
    開(kāi)封第一講書(shū)人閱讀 52,475評(píng)論 1 312
  • 那天欣喧,我揣著相機(jī)與錄音,去河邊找鬼梯找。 笑死唆阿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的锈锤。 我是一名探鬼主播驯鳖,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼久免!你這毒婦竟也來(lái)了浅辙?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤阎姥,失蹤者是張志新(化名)和其女友劉穎记舆,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體呼巴,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泽腮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了衣赶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诊赊。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖府瞄,靈堂內(nèi)的尸體忽然破棺而出碧磅,到底是詐尸還是另有隱情,我是刑警寧澤摘能,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布续崖,位于F島的核電站,受9級(jí)特大地震影響团搞,放射性物質(zhì)發(fā)生泄漏严望。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一逻恐、第九天 我趴在偏房一處隱蔽的房頂上張望像吻。 院中可真熱鬧,春花似錦复隆、人聲如沸拨匆。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)惭每。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間台腥,已是汗流浹背宏赘。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留黎侈,地道東北人察署。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像峻汉,于是被迫代替她去往敵國(guó)和親贴汪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

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

  • 一休吠、概述 ??代理模式我們接觸的就比較多了扳埂,所謂的代理模式就是,給某一個(gè)對(duì)象提供一個(gè)代理對(duì)象蛛碌,并由代理對(duì)象控制對(duì)原...
    騎著烏龜去看海閱讀 910評(píng)論 0 9
  • 一聂喇、靜態(tài)代理我們先來(lái)編寫(xiě)一個(gè)業(yè)務(wù)接口:package proxy.part01;/** 核心業(yè)務(wù)接口/public...
    Damon_Lu閱讀 847評(píng)論 0 0
  • 歷程萬(wàn)里塵風(fēng)走送辖源,濃思了卻厚土沉沉蔚携。 乘風(fēng)般的的速度,映著晨起的霞光克饶,在時(shí)光碾轉(zhuǎn)的下一刻酝蜒,回訪最初的相逢。源上世相...
    馮少閱讀 292評(píng)論 0 1
  • 日月臨回矾湃,光陰如梭亡脑。婉婉綿長(zhǎng),而即沖沖略過(guò)邀跃,轉(zhuǎn)眼間老年己俏然步入我的歲月之列霉咨,來(lái)不及嘆一聲不惑之慮,抬頭望長(zhǎng)空拍屑、滿...
    快樂(lè)天成閱讀 238評(píng)論 0 4
  • 《何以笙簫默》里僵驰,何以琛對(duì)單戀自己許久的以玫說(shuō)喷斋,“以后你就會(huì)明白,如果這個(gè)世界上蒜茴,有一個(gè)人出現(xiàn)過(guò)星爪,那么其他人都會(huì)變...
    小竇初開(kāi)閱讀 970評(píng)論 0 0