設(shè)計模式(七-擴展篇)——動態(tài)代理

本文屬于系列文章《設(shè)計模式》氢哮,附上文集鏈接

本文集代理模式請看此處

前言

上一篇寫的那個代理模式觅赊,是屬于靜態(tài)代理。假設(shè)一種場景,假設(shè)我們要為很多類的很多方法添加前置和后置方法(別忘了代理模式是為了控制)粱腻,按照靜態(tài)代理的方法,我們就得為所有類的所有方法一個一個地去實現(xiàn)需求斩跌,想想這工程量就覺得可怕绍些,所以就有了很優(yōu)美的實現(xiàn)方式,叫做動態(tài)代理滔驶。

JDK代理

且看代碼

// 抽象接口
public interface MyInterface {
    void hello(int i, int j);
    void hello(int i, int j, int k);
}
// 接口實現(xiàn)類遇革,也是我們的代理目標類
public class MySubject implements MyInterface{
    @Override
    public void hello(int i, int j) {
        System.out.println("hello(): "+i+"-"+j);
    }
    @Override
    public void hello(int i, int j, int k) {
        System.out.println("hello(): "+i+"-"+j+"~"+k);
    }
}
// 這個是關(guān)鍵,被代理對象方法的實際執(zhí)行地方揭糕,通過反射實現(xiàn)
public class MyInvocationHandler implements InvocationHandler{
    // 被代理的目標對象
    private Object target;
    MyInvocationHandler(Object target) {  
        super();  
        this.target = target;  
    } 
    @Override
    /\*\*
     \* proxy 代理的目標對象
     \* method 代理的目標對象的方法
     \*  args萝快,方法的參數(shù)
     \*/
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法: " + method.getName() + "執(zhí)行前");
        StringBuilder sb = new StringBuilder("方法的參數(shù):");
        for (Object arg : args) {
            sb.append(arg.toString()+" ");
        }
        System.out.println(sb.toString());
        Object result = method.invoke(target, args);
        System.out.println("方法: " + method.getName() + "執(zhí)行后");
        return result;
    }
}
// 場景類
public class Client {
    public static void main(String[] args) {
        MyInterface mySubject = new MySubject();
        MyInvocationHandler handler = new MyInvocationHandler(mySubject);
        MyInterface mySubjectProxy = (MyInterface) Proxy.newProxyInstance(mySubject.getClass().getClassLoader(),  
                mySubject.getClass().getInterfaces(), handler);
proxy.getProxyObject();
        mySubjectProxy.hello(5,98);
        System.out.println("\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*");
        mySubjectProxy.hello(5, 23, 54);
    }
}
結(jié)果:
方法: hello執(zhí)行前
方法的參數(shù):5 98 
hello(): 5-98
方法: hello執(zhí)行后
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
方法: hello執(zhí)行前
方法的參數(shù):5 23 54 
hello(): 5-23~54
方法: hello執(zhí)行后

比較關(guān)鍵的就三個地方:

  • 一、被代理的目標對象必須實現(xiàn)接口著角,即上文的<code>MySubject</code>和<code>MyInterface</code>揪漩。
  • 二、實現(xiàn)InvocationHandler接口的handler類吏口,即上文中的<code>public class MyInvocationHandler implements InvocationHandler</code>奄容。方法的參數(shù)說明在上面也有了冰更,這里就不在闡述了。
  • 三昂勒、使用Proxy類獲取到目標對象的代理對象蜀细,即上文中的Proxy.newProxyInstance(mySubject.getClass().getClassLoader(),mySubject.getClass().getInterfaces(), handler);

之前也想過Proxy是怎么實現(xiàn)這個代理過程的,結(jié)果在這里看到了戈盈。
回到上面的代碼奠衔,其實上面的代碼還可以封裝下,比如變成下面的樣子:

// 接口類塘娶,接口實現(xiàn)類归斤,handler類都沒變化,引入下面的類
// 簡單的動態(tài)代理類刁岸,傳入一個目標對象參數(shù)脏里,得到其代理對象
public class DynamicProxy{
    private MyInvocationHandler handler;
    private Object target;
    public DynamicProxy(Object _target) {
        this.target = _target;
        handler = new MyInvocationHandler(_target);
    }
    public Object getProxyObject(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
    }
}
// 場景類
public class Client {
    public static void main(String[] args) {
        MyInterface mySubject = new MySubject();
        **DynamicProxy proxy = new DynamicProxy(mySubject);
        MyInterface mySubjectProxy = (MyInterface) proxy.getProxyObject();**
        mySubjectProxy.hello(5,98);
        System.out.println("******************");
        mySubjectProxy.hello(5, 23, 54);
    }
}
結(jié)果:
方法: hello執(zhí)行前
方法的參數(shù):5 98 
hello(): 5-98
方法: hello執(zhí)行后
*********************
方法: hello執(zhí)行前
方法的參數(shù):5 23 54 
hello(): 5-23~54
方法: hello執(zhí)行后

上面場景類的代碼,用自然語言解釋挺流暢的虹曙,就是創(chuàng)建代理類對象迫横,通過代理類對象獲取被代理對象的代理對象。反正封裝方法多種多樣根吁,上面其實其中一種员淫,有意者多試試唄合蔽。
上面就是JDK代理击敌,但是JDK代理有一個限制,就是上面說到的關(guān)鍵點的第一點拴事,JDK代理無法脫離接口而實現(xiàn)沃斤,要為某個目標對象實現(xiàn)JDK代理,該目標對象至少實現(xiàn)一個接口刃宵,那如果要為沒實現(xiàn)接口的類實現(xiàn)代理衡瓶,怎么辦,就是我們的Cglib代理牲证。

cglib代理

且看代碼

// 我們的代理目標類哮针,這次是沒有實現(xiàn)接口的
public class MySubject {
    public void hello(int i, int j) {
        System.out.println("hello(): "+i+"-"+j);
    }
    public void hello(int i, int j, int k) {
        System.out.println("hello(): "+i+"-"+j+"~"+k);
    }
}
// cglib代理類
public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("方法: " + method.getName() + "執(zhí)行前");
        StringBuilder sb = new StringBuilder("方法的參數(shù):");
        for (Object arg : args) {
            sb.append(arg.toString() + " ");
        }
        System.out.println(sb.toString());
        Object obj = methodProxy.invokeSuper(o, args);
        System.out.println("方法: " + method.getName() + "執(zhí)行后");
        return obj;
    }
}
// 場景類
public class Client {
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(MySubject.class);
        enhancer.setCallback(cglibProxy);
        // 得到代理對象
        MySubject o = (MySubject) enhancer.create();
        o.hello(5, 98);
        System.out.println("******************");
        o.hello(5, 23, 54);
    }
}
結(jié)果:
方法: hello執(zhí)行前
方法的參數(shù):5 98 
hello(): 5-98
方法: hello執(zhí)行后
**********************
方法: hello執(zhí)行前
方法的參數(shù):5 23 54 
hello(): 5-23~54
方法: hello執(zhí)行后

cglib是提供動態(tài)代理的第三方庫,并不是jdk給我們代理坦袍,所以需要自己解決導(dǎo)包的問題十厢。
這里,cglib設(shè)計的幾個核心類捂齐,分別是:

  • net.sf.cglib.proxy.Enhancer – 主要的增強類
  • net.sf.cglib.proxy.MethodInterceptor – 主要的方法攔截類蛮放,它是Callback接口的子接口,需要用戶實現(xiàn)
  • net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method類的代理類奠宜,可以方便的實現(xiàn)對源對象方法的調(diào)用

核心方法就是<code>public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable </code>,第一個參數(shù)是代理對象包颁,第二和第三個參數(shù)分別是攔截的方法和方法的參數(shù)瞻想,第四個是方法的代理對象。這個就是cglib代理娩嚼。

以上就是動態(tài)代理蘑险。

水平有限,難免有錯岳悟,還請評論區(qū)指責(zé)下

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末漠其,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子竿音,更是在濱河造成了極大的恐慌和屎,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件春瞬,死亡現(xiàn)場離奇詭異柴信,居然都是意外死亡,警方通過查閱死者的電腦和手機宽气,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門随常,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人萄涯,你說我怎么就攤上這事绪氛。” “怎么了涝影?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵枣察,是天一觀的道長。 經(jīng)常有香客問我燃逻,道長序目,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任伯襟,我火速辦了婚禮猿涨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘姆怪。我一直安慰自己叛赚,他們只是感情好,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布稽揭。 她就那樣靜靜地躺著俺附,像睡著了一般。 火紅的嫁衣襯著肌膚如雪淀衣。 梳的紋絲不亂的頭發(fā)上昙读,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音膨桥,去河邊找鬼蛮浑。 笑死唠叛,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的沮稚。 我是一名探鬼主播艺沼,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蕴掏!你這毒婦竟也來了障般?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤盛杰,失蹤者是張志新(化名)和其女友劉穎挽荡,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體即供,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡定拟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了逗嫡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片青自。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖驱证,靈堂內(nèi)的尸體忽然破棺而出延窜,到底是詐尸還是另有隱情,我是刑警寧澤抹锄,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布逆瑞,位于F島的核電站,受9級特大地震影響祈远,放射性物質(zhì)發(fā)生泄漏呆万。R本人自食惡果不足惜商源,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一车份、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧牡彻,春花似錦扫沼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至总寻,卻和暖如春器罐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背渐行。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工轰坊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留铸董,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓肴沫,卻偏偏與公主長得像粟害,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子颤芬,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理悲幅,服務(wù)發(fā)現(xiàn),斷路器站蝠,智...
    卡卡羅2017閱讀 134,600評論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法汰具,類相關(guān)的語法,內(nèi)部類的語法菱魔,繼承相關(guān)的語法郁副,異常的語法,線程的語...
    子非魚_t_閱讀 31,587評論 18 399
  • 一> 代理模式 概述 代理(Proxy)是一種設(shè)計模式豌习, 提供了對目標對象另外的訪問方式存谎;即通過代理訪問目標對象。...
    奮斗的老王閱讀 1,101評論 0 50
  • 這今天,#田生萬物#的一群女人被一個女人感動著栋艳,她是鄭州親子盟恰聘、田生萬物創(chuàng)始人草莓,一個孩子的母親吸占,被大家親切的稱...
    吃貨蘇小妹閱讀 242評論 0 0
  • 你聽過卓依婷的那首經(jīng)典歌曲《電話情思》嗎?歌詞里是這么唱的:好想好想給你打個電話件蚕,也想和你悄悄地說些知心話孙技。一遍一...
    可桐閱讀 454評論 4 7