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

代理模式提供了對(duì)目標(biāo)對(duì)象的另外的訪問方式旭咽,相較于直接訪問對(duì)象,通過代理訪問對(duì)象可以在目標(biāo)對(duì)象的功能基礎(chǔ)上對(duì)目標(biāo)對(duì)象的功能經(jīng)行擴(kuò)展废登。

靜態(tài)代理

靜態(tài)代理的目標(biāo)對(duì)象和代理對(duì)象都實(shí)現(xiàn)一個(gè)共同的接口或者繼承一個(gè)共同的父類革娄,這樣就可以通過調(diào)用代理對(duì)象的相同方法來調(diào)用目標(biāo)對(duì)象翘地。

舉個(gè)栗子:
UserDao.java

public interface UserDao {
    public void save();
}

UserDaoImpl.java

public class UserDaoImpl implements UserDao{

    @Override
    public void save() {
        System.out.println("save...");
    }
}

UserDaoProxy.java

public class UserDaoProxy implements UserDao{
    private UserDao user;
    public UserDaoProxy(UserDao user) {
        this.user = user;
    }
    @Override
    public void save() {
        System.out.println("preparing for save...");
        user.save();
        System.out.println("save complete!");
    }
}

ProxyTest.java

public class ProxyTest {
    public static void main(String[] args) {
        UserDao user = new UserDaoImpl();
        UserDaoProxy proxy = new UserDaoProxy(user);
        proxy.save();
    }
}

輸出結(jié)果:

preparing for save...
save...
save complete!

靜態(tài)代理雖然能實(shí)現(xiàn)在不修改目標(biāo)對(duì)象的功能的前提下進(jìn)行擴(kuò)展,但是滚粟,試想一下寻仗,如果目標(biāo)對(duì)象需要增加一個(gè)方法的話,要怎么修改呢凡壤?答案是要修改目標(biāo)類和所有目標(biāo)類的代理類(當(dāng)然還有它們實(shí)現(xiàn)的接口)署尤!這樣維護(hù)的代價(jià)是很大的。動(dòng)態(tài)代理就可以解決這個(gè)問題亚侠。

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

動(dòng)態(tài)代理不需要實(shí)現(xiàn)接口曹体,只需要接口,我們利用jdk中的api可以動(dòng)態(tài)地在內(nèi)存中構(gòu)建代理的對(duì)象硝烂,java的動(dòng)態(tài)代理也叫jdk代理或者接口代理箕别。

我們可以直接利用上邊的UserDao接口對(duì)動(dòng)態(tài)代理進(jìn)行測(cè)試,舉個(gè)栗子:

ProxyFactory.java

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

public class ProxyFactory {
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    public Object getProxyInstance() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("preparing for save... ...");
                        Object returnValue = method.invoke(target, args);
                        System.out.println("save complete!!");
                        return returnValue;
                    }
                });
    }
}

DynamicProxyTest.java

public class DynamicProxyTest {
    public static void main(String[] args) {
        UserDao user = new UserDaoImpl();
        UserDao proxy = (UserDao) new ProxyFactory(user).getProxyInstance();
        proxy.save();
    }
}

輸出結(jié)果:

preparing for save... ...
save...
save complete!!

這里就有很多要說明的地方了首先Proxy.newProxyInstance這個(gè)方法滞谢,返回的是一個(gè)經(jīng)過擴(kuò)展了的代理類串稀,這個(gè)方法需要三個(gè)參數(shù):

  • ClassLoader loader:這個(gè)參數(shù)是要目標(biāo)類的類加載器,任何類都可以通過.getClass().getClassLoader()獲得它自己的類加載器(其實(shí)通過我們?cè)趧?dòng)態(tài)工廠中設(shè)定的目標(biāo)類為Object就可得知)
  • Class<?>[] interfaces:一樣狮杨,任何類都可以通過.getClass().getInterfaces()來獲得它實(shí)現(xiàn)的所有接口
  • InvocationHandler h:這個(gè)接口只定義了一個(gè)方法:invoke母截,這個(gè)是用來處理代理實(shí)例上的方法,我們直接緊跟著在這個(gè)參數(shù)后邊實(shí)現(xiàn)了它橄教。

jdk1.8的文檔上對(duì)InvocationHandler接口的描述是這樣的:

每個(gè)代理實(shí)例都有一個(gè)關(guān)聯(lián)的調(diào)用處理程序清寇。當(dāng)在代理實(shí)例上調(diào)用方法時(shí),方法條用將被編碼并分配到其調(diào)用處理程序的invoke方法颤陶。

也就是說颗管,當(dāng)我們執(zhí)行代理實(shí)例proxy的save方法時(shí),我們實(shí)際上是執(zhí)行了我們重寫的invoke方法滓走。InvocationHandler的invoke方法和Method的invoke方法要進(jìn)行區(qū)分:在我們重寫的invoke方法里垦江,method.invoke 執(zhí)行的是target關(guān)聯(lián)的實(shí)例的對(duì)應(yīng)方法,args表示方法所需要的參數(shù)搅方。

這樣比吭,我們通過ProxyFactory.getProxyInstance()方法,就得到了一個(gè)擴(kuò)展過的目標(biāo)類的代理類姨涡。

靜態(tài)代理和動(dòng)態(tài)代理都需要目標(biāo)類實(shí)現(xiàn)一個(gè)接口衩藤,但如果我們的目標(biāo)類是一個(gè)單獨(dú)的沒有實(shí)現(xiàn)任何接口的對(duì)象怎么辦?這時(shí)候我們就可以使用Cglib代理涛漂。

Cglib代理

Cglib代理通過創(chuàng)建目標(biāo)對(duì)象的子類來實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象的代理赏表,所以也叫做子類代理检诗。

因?yàn)閟pring中包括了cglib的功能,所以我們可以通過引入spring的spring-core-4.3.6.RELEASE.jar包來使用cglib瓢剿。

上栗子:

CustomerDao.java

public class CustomerDao {
    public void save() {
        System.out.println("save...");
    }
}

CglibFactory.java

import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class CglibFactory implements MethodInterceptor {
    private Object target;

    public CglibFactory(Object target) {
        this.target = target;
    }

    // 代理方法
    public Object createProxy() {
        // 創(chuàng)建一個(gè)動(dòng)態(tài)對(duì)象
        Enhancer hancer = new Enhancer();
        // 確定要增強(qiáng)的類(目標(biāo)對(duì)象)逢慌,設(shè)置其為父類
        hancer.setSuperclass(target.getClass());
        // 添加回調(diào)函數(shù)(?)
        hancer.setCallback(this);
        // 返回創(chuàng)建的代理類
        return hancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("prepering for save... ... ...");
        Object returnValue = method.invoke(target, args);
        System.out.println("saving completed!!!");
        return returnValue;
    }
}

CglibTest.java

public class CglibTest {
    public static void main(String[] args) {
        CustomerDao customer = new CustomerDao();
        CustomerDao proxy = (CustomerDao) new CglibFactory(customer).createProxy();
        proxy.save();
    }
}

輸出結(jié)果:

prepering for save... ... ...
save...
saving completed!!!

這里的intercept方法類似于動(dòng)態(tài)代理的invoke方法间狂,就不多加敘述了攻泼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鉴象,隨后出現(xiàn)的幾起案子忙菠,更是在濱河造成了極大的恐慌,老刑警劉巖纺弊,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件牛欢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡俭尖,警方通過查閱死者的電腦和手機(jī)氢惋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來稽犁,“玉大人焰望,你說我怎么就攤上這事∫押ィ” “怎么了熊赖?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)虑椎。 經(jīng)常有香客問我震鹉,道長(zhǎng),這世上最難降的妖魔是什么捆姜? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任传趾,我火速辦了婚禮,結(jié)果婚禮上泥技,老公的妹妹穿的比我還像新娘浆兰。我一直安慰自己,他們只是感情好珊豹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布簸呈。 她就那樣靜靜地躺著,像睡著了一般店茶。 火紅的嫁衣襯著肌膚如雪蜕便。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天贩幻,我揣著相機(jī)與錄音轿腺,去河邊找鬼两嘴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛吃溅,可吹牛的內(nèi)容都是我干的溶诞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼决侈,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了喧务?” 一聲冷哼從身側(cè)響起赖歌,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎功茴,沒想到半個(gè)月后庐冯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坎穿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年展父,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片玲昧。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡栖茉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出孵延,到底是詐尸還是另有隱情吕漂,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布尘应,位于F島的核電站惶凝,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏犬钢。R本人自食惡果不足惜苍鲜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望玷犹。 院中可真熱鬧混滔,春花似錦、人聲如沸箱舞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)晴股。三九已至愿伴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間电湘,已是汗流浹背隔节。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工鹅经, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人怎诫。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓瘾晃,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親幻妓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蹦误,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

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

  • 定義 代理模式是對(duì)象的結(jié)構(gòu)模式。代理模式給某一個(gè)對(duì)象提供代理對(duì)象肉津,并由代理對(duì)象控制對(duì)源對(duì)象的引用强胰。 代理模式的結(jié)構(gòu)...
    步積閱讀 6,394評(píng)論 0 1
  • 最近看到一篇關(guān)于講代理的文章,接觸到了一些jdk動(dòng)態(tài)代理妹沙,然后寫一篇文章加深下記憶偶洋。主要講靜態(tài)代理和動(dòng)態(tài)代理,通俗...
    浪漫宇宙與人間日常閱讀 303評(píng)論 0 0
  • 動(dòng)態(tài)代理的意義在于生成一個(gè)占位(又稱代理對(duì)象)距糖,來代理真實(shí)對(duì)象玄窝,從而控制真實(shí)對(duì)象的訪問。 我們首先來談?wù)勈裁词谴?..
    Haozz_1994閱讀 305評(píng)論 0 2
  • 代理 在談代理模式之前悍引,有必要先了解什么是代理恩脂。其實(shí)代理在日常生活中很常見。比如吗铐,海外商品備受國(guó)人青睞东亦,但一般人(...
    Tomandy閱讀 312評(píng)論 0 0
  • 從事過培訓(xùn)工作的人們應(yīng)該知道,良好的培訓(xùn)開場(chǎng)唬渗,不僅可以拉近與學(xué)員之間的距離典阵,更可以有效的提高學(xué)員的學(xué)習(xí)意愿和參...
    余仲泉閱讀 1,587評(píng)論 0 5