代理模式之靜態(tài)代理规脸,JDK動態(tài)代理和cglib動態(tài)代理

代理模式熊咽,顧名思義,就是通過代理去完成某些功能横殴。比如被因,你需要購買火車票,不想跑那么遠到火車站售票窗口買衫仑,可以去附近的火車票代售點買梨与,或者到攜程等第三方網(wǎng)站買文狱。這個時候,我們就把火車站叫做目標(biāo)對象或者委托對象瞄崇,也可以叫被代理對象壕曼,而火車票代售點和攜程就叫做代理對象等浊。

一、靜態(tài)代理

靜態(tài)代理是最簡單的代理模式筹燕。需要定義一個接口,然后委托類和代理類分別實現(xiàn)這個接口

//待實現(xiàn)的接口
public interface UserManager {
    public void getName(String name);
    public void getId(int id);
}
//委托類
public class UserManagerImpl implements UserManager {

    @Override
    public void getName(String name) {
        System.out.println("UserManagerImpl.getName:" + name);
    }

    @Override
    public void getId(int id) {
        System.out.println("UserManagerImpl.getId:" + id);
    }
}
//代理類
public class UserManagerProxy implements UserManager {

    UserManager userManager;

    public UserManagerProxy(UserManager userManager) {
        this.userManager = userManager;
    }

    @Override
    public void getName(String name) {
        System.out.println("before getName");
        userManager.getName(name);
        System.out.println("after getName");
    }

    @Override
    public void getId(int id) {
        userManager.getId(id);
    }

    public static void main(String[] args) {
        UserManagerProxy proxy = new UserManagerProxy(new UserManagerImpl());
        proxy.getName("zhangsan");
    }
}
//before getName
//UserManagerImpl.getName:zhangsan
//after getName

可以看到过咬,在編譯成class之前糠涛,就已經(jīng)確定了委托類UserManagerImpl和代理類UserManagerProxy。因此集漾,才叫靜態(tài)代理。這樣雖然定義比較方便具篇,實現(xiàn)也簡單凌埂,但是有一個弊端。當(dāng)接口再新加一個方法時瞳抓,委托類和代理類都需要同步地去實現(xiàn)方法,因此維護起來比較麻煩栓霜。而動態(tài)代理解決了這個問題横蜒。

二、JDK動態(tài)代理

動態(tài)代理分為JDK動態(tài)代理和cglib動態(tài)代理丛晌。動態(tài)是指,代理類是通過反射等機制動態(tài)生成的抚垄,委托類和代理類的關(guān)系在運行時才確定。他們的主要區(qū)別就是督勺,JDK動態(tài)代理需要實現(xiàn)接口渠羞,而cglib是通過繼承來實現(xiàn)的智哀,不需要定義接口荧恍。

JDK動態(tài)代理,需要定義一個類去實現(xiàn)InvocationHandler接口

public class LogHandler implements InvocationHandler {

    private Object targetObj;

    public Object newProxyObject(Object targetObj){
        this.targetObj = targetObj;
        return Proxy.newProxyInstance(
                targetObj.getClass().getClassLoader(), //獲取委托類的類加載器
                targetObj.getClass().getInterfaces(), //獲取委托類實現(xiàn)的所有接口
                this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object ret;

        try {
            System.out.println("before method");
            ret = method.invoke(targetObj, args);
            System.out.println("after method");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            System.out.println("error");
            throw e;
        }

        return ret;
    }
}

public class TestProxy {
    public static void main(String[] args) {
        LogHandler logHandler = new LogHandler();
        UserManager o = (UserManager)logHandler.newProxyObject(new UserManagerImpl());
        o.getName("ls");
        o.getId(2);
    }
}
/**
運行結(jié)果如下:
before method
UserManagerImpl.getName:ls
after method
before method
UserManagerImpl.getId:2
after method
*/

JDK動態(tài)代理其實是在運行時動態(tài)生成了一個代理類去實現(xiàn)接口摹菠,只是隱藏了這個過程骗爆,我們不知道而已。

class $JDKProxy implements UserManager{}

需要注意的是煮寡,實現(xiàn)JDK動態(tài)代理的一個前提就是犀呼,需要定義一個接口幸撕,然后委托類去實現(xiàn)這個接口外臂。那如果我不想定義接口,只定義一個委托類能不能實現(xiàn)呢貌矿?這就需要用到cglib代理了罪佳。(因為cglib是通過繼承方式)

三、cglib動態(tài)代理

需要定義一個類實現(xiàn)MethodInterceptor接口(注意菇民,這個類可不是代理類,也不是委托類哦)阔馋。

//委托類娇掏,不需要實現(xiàn)接口
public class CgTarget {
    public void getContent(){
        System.out.println("cglib被代理類getContent方法");
    }
}

public class CglibProxy implements MethodInterceptor {

    private Object target;

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib開始");
        Object invoke = method.invoke(target, objects);
        System.out.println("cglib結(jié)束");
        return invoke;
    }
    //代理方法
    public Object getCglibProxy(Object target){
        this.target = target;
        Enhancer enhancer = new Enhancer();
        //設(shè)置增強類的父類,即被代理類
        enhancer.setSuperclass(target.getClass());
        //設(shè)置回調(diào)函數(shù)
        enhancer.setCallback(this);
        //返回創(chuàng)建的代理類
        return enhancer.create();
    }
}

public class TestCglib {
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        CgTarget o = (CgTarget)cglibProxy.getCglibProxy(new CgTarget());
        o.getContent();
    }
}
/**
打印結(jié)果如下:
cglib開始
cglib被代理類getContent方法
cglib結(jié)束
*/

可以看到,cglib動態(tài)代理是通過Enhancer類的create方法創(chuàng)建了代理類下梢。其實,其內(nèi)部是通過繼承委托類來動態(tài)生成代理類的孽江。它隱藏了以下過程

class $cglibProxy extends CgTarget{}

因此,委托類不能定義成final類型的辆琅,因為final修飾的類是不能被繼承的这刷。

了解spring AOP的同學(xué)應(yīng)該知道,AOP是面向切面編程暇屋,在管理事物的時候會用到。其實昙衅,AOP就是通過動態(tài)代理來實現(xiàn)的本辐,具體是用的JDK動態(tài)代理還是cglib動態(tài)代理漱抓,感興趣的小伙伴可以繼續(xù)深入研究哦奖亚。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末甜橱,一起剝皮案震驚了整個濱河市躯泰,隨后出現(xiàn)的幾起案子谭羔,更是在濱河造成了極大的恐慌麦向,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件话告,死亡現(xiàn)場離奇詭異卵慰,居然都是意外死亡,警方通過查閱死者的電腦和手機病线,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來送挑,“玉大人,你說我怎么就攤上這事纺裁∩耐唬” “怎么了区赵?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長漱受。 經(jīng)常有香客問我,道長昂羡,這世上最難降的妖魔是什么摔踱? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮蛹批,結(jié)果婚禮上篮愉,老公的妹妹穿的比我還像新娘腐芍。我一直安慰自己试躏,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布泣刹。 她就那樣靜靜地躺著犀被,像睡著了一般。 火紅的嫁衣襯著肌膚如雪弱判。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天开伏,我揣著相機與錄音,去河邊找鬼捅伤。 笑死巫玻,一個胖子當(dāng)著我的面吹牛丛忆,可吹牛的內(nèi)容都是我干的仍秤。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼凰浮,長吁一口氣:“原來是場噩夢啊……” “哼苇本!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瓣窄,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤裳凸,失蹤者是張志新(化名)和其女友劉穎啥么,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悬荣,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡氯迂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了嚼蚀。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡弄捕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出守谓,到底是詐尸還是另有隱情,我是刑警寧澤荞雏,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布平酿,位于F島的核電站,受9級特大地震影響蜈彼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜挖垛,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一秉颗、第九天 我趴在偏房一處隱蔽的房頂上張望送矩。 院中可真熱鬧,春花似錦栋荸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽呼伸。三九已至钝尸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間铃辖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工娇斩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人犬第。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓瓶殃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親遥椿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,472評論 2 348

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