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

轉(zhuǎn)自Java3y公眾號(hào)绳姨,僅個(gè)人學(xué)習(xí)使用

一佃牛、代理模式介紹

代理模式簡(jiǎn)介

二、用代碼描述代理模式(靜態(tài)代理)

這里有一個(gè)程序員接口床未,他們每天就是寫代碼

public interface Programmer {

    // 程序員每天都寫代碼
    void coding();

}

Java3y也是一個(gè)程序員竭翠,他也寫代碼(每個(gè)程序員寫的代碼都不一樣,所以分了接口和實(shí)現(xiàn)類)

public class Java3y implements Programmer {

    @Override
    public void coding() {
        System.out.println("Java3y最新文章:......給女朋友講解什么是代理模式.......");
    }
}

此時(shí)Java3y已經(jīng)是一個(gè)網(wǎng)紅了薇搁,他不想枯燥地寫代碼斋扰。他在想:“在寫代碼時(shí)能賺錢就好咯,有人給我錢,我才寫代碼”传货。但是屎鳍,Java3y的文筆太爛了,一旦有什么冬瓜豆腐问裕,分分鐘變成過(guò)氣網(wǎng)紅逮壁,這是Java3y不愿意看到的。

而知乎粮宛、博客園這種平臺(tái)又不能自己給自己點(diǎn)贊來(lái)吸引流量(-->當(dāng)前對(duì)象無(wú)法做)

所以Java3y去請(qǐng)了一個(gè)程序員大V(代理)來(lái)實(shí)現(xiàn)自己的計(jì)劃窥淆,這個(gè)程序員大V會(huì)每次讓Java3y發(fā)文章時(shí),就給Java3y點(diǎn)贊窟勃、評(píng)論祖乳、鼓吹這文章好。只要流量有了秉氧,錢就到手了眷昆。

public class ProgrammerBigV implements Programmer {

    // 指定程序員大V要讓誰(shuí)發(fā)文章(先發(fā)文章、后點(diǎn)贊)
    private Java3y java3y ;

    public ProgrammerBigV(Java3y java3y) {
        this.java3y = java3y;
    }

    /yun 程序員大V點(diǎn)贊評(píng)論收藏轉(zhuǎn)發(fā)
    public void upvote() {
        System.out.println("程序員大V點(diǎn)贊評(píng)論收藏轉(zhuǎn)發(fā)汁咏!");
    }

    @Override
    public void coding() {

        // 讓Java3y發(fā)文章
        java3y.coding();

        // 程序員大V點(diǎn)贊評(píng)論收藏轉(zhuǎn)發(fā)亚斋!
        upvote();
    }
}

文章(代碼)還是由Java3y來(lái)發(fā),但每次發(fā)送之后程序員大V都會(huì)點(diǎn)贊攘滩。

public class Main {

    public static void main(String[] args) {

        // 想要發(fā)達(dá)的Java3y
        Java3y java3y = new Java3y();

        // 受委托程序員大V
        Programmer programmer = new ProgrammerBigV(java3y);

        // 受委托程序員大V讓Java3y發(fā)文章帅刊,大V(自己)來(lái)點(diǎn)贊
        programmer.coding();
    }  
}
這樣一來(lái),不明真相的路人就覺得Java3y是真厲害漂问,知識(shí)付費(fèi)赖瞒。

2.1透明代理(普通代理)

經(jīng)過(guò)一段時(shí)間,Java3y嘗到甜頭了蚤假,覺得這是一條財(cái)路栏饮。于是Java3y給足了程序員大V錢,讓程序員大V只做他的生意磷仰,不能做其他人的生意(斷了其他人的財(cái)路)袍嬉。

于是乎,程序員大V做Java3y一個(gè)人的生意:

public class ProgrammerBigV implements Programmer {

    // 指定程序員大V要給Java3y點(diǎn)贊
    private Java3y java3y ;

    // 只做Java3y的生意了
    public ProgrammerBigV() {
        this.java3y = new Java3y();
    }

    // 程序員大V點(diǎn)贊評(píng)論收藏轉(zhuǎn)發(fā)
    public void upvote() {
        System.out.println("程序員大V點(diǎn)贊評(píng)論收藏轉(zhuǎn)發(fā)灶平!");
    }

    @Override
    public void coding() {

        // 讓Java3y發(fā)文章了
        java3y.coding();

        // 程序員大V點(diǎn)贊評(píng)論收藏轉(zhuǎn)發(fā)伺通!
        upvote();

    }
}

于是乎,程序員大V想要賺點(diǎn)零花錢的時(shí)候直接讓Java3y發(fā)文章就好了逢享。

public class Main {

    public static void main(String[] args) {

        // 受委托程序員大V
        Programmer programmer = new ProgrammerBigV();

        // 受委托程序員大V讓Java3y發(fā)文章罐监,大V來(lái)點(diǎn)贊
        programmer.coding();

    }
}

此時(shí),真實(shí)對(duì)象(Java3y)對(duì)外界來(lái)說(shuō)是透明的瞒爬。

2.2代理類自定義方法

程序員大V看到Java3y一直順風(fēng)順?biāo)ψ纾嵈箦X了调缨。覺得是時(shí)候要加價(jià)了,于是在點(diǎn)贊完畢后就跟Java3y說(shuō)每點(diǎn)完一次贊加100塊吆你!

于是乎弦叶,程序員大V就增添了另外一個(gè)方法:addMoney()

public class ProgrammerBigV implements Programmer {


    // ..省略了上面的代碼

    // 加價(jià)啦
    public void addMoney() {
        System.out.println("這次我要加100塊");
    }

    @Override
    public void coding() {

        // 讓Java3y發(fā)文章了
        java3y.coding();

        // 程序員大V點(diǎn)贊評(píng)論收藏轉(zhuǎn)發(fā)!
        upvote();

        // 加價(jià)
        addMoney();

    }
}

于是乎程序員大V每次都能多100塊:


運(yùn)行結(jié)果圖

三妇多、動(dòng)態(tài)代理

幾年時(shí)間過(guò)去了伤哺,Java3y靠著程序員的大V點(diǎn)贊還是沒發(fā)財(cái)(本質(zhì)上Java3y還沒有干貨,沒受到大眾的認(rèn)可)者祖。此時(shí)已經(jīng)有很多人晉升成了程序員大V了立莉,但是之前的那個(gè)程序員大V還是一直累加著錢…雖然在開始的時(shí)候Java3y嘗到了甜頭,但現(xiàn)在Java3y財(cái)政已經(jīng)匱乏了七问。

Java3y將自己的失敗認(rèn)為:一定是那個(gè)程序員大V轉(zhuǎn)門為我點(diǎn)贊被識(shí)破了蜓耻,吃瓜群眾都知道了,他收費(fèi)又那么貴械巡。

于是Java3y不請(qǐng)程序員大V了刹淌,請(qǐng)水軍來(lái)點(diǎn)贊(水軍便宜,只要能點(diǎn)贊就行了):

public class Main {

    public static void main(String[] args1) {

        // Java3y請(qǐng)水軍
        Java3y java3y = new Java3y();

        Programmer programmerWaterArmy = (Programmer) Proxy.newProxyInstance(java3y.getClass().getClassLoader(), java3y.getClass().getInterfaces(), (proxy, method, args) -> {

            // 如果是調(diào)用coding方法讥耗,那么水軍就要點(diǎn)贊了
            if (method.getName().equals("coding")) {
                method.invoke(java3y, args);
                System.out.println("我是水軍有勾,我來(lái)點(diǎn)贊了!");

            } else {
                // 如果不是調(diào)用coding方法古程,那么調(diào)用原對(duì)象的方法
                return method.invoke(java3y, args);
            }

            return null;
        });

        // 每當(dāng)Java3y寫完文章蔼卡,水軍都會(huì)點(diǎn)贊
        programmerWaterArmy.coding();

    }

}

每當(dāng)Java3y發(fā)文章的時(shí)候,水軍都會(huì)點(diǎn)贊挣磨。


運(yùn)行效果圖

Java3y感嘆:請(qǐng)水軍真是方便啊~

3.1動(dòng)態(tài)代理調(diào)用過(guò)程

我們來(lái)看看究竟是怎么請(qǐng)水軍的:

Java提供了一個(gè)Proxy類雇逞,調(diào)用它的newInstance方法可以生成某個(gè)對(duì)象的代理對(duì)象,該方法需要三個(gè)參數(shù):
我們來(lái)看看究竟是怎么請(qǐng)水軍的:

Proxy類

參數(shù)一:生成代理對(duì)象使用哪個(gè)類裝載器【一般我們使用的是被代理類的裝載器】

?參數(shù)二:生成哪個(gè)對(duì)象的代理對(duì)象,通過(guò)接口指定【指定要被代理類的接口】

?參數(shù)三:生成的代理對(duì)象的方法里干什么事【實(shí)現(xiàn)handler接口茁裙,我們想怎么實(shí)現(xiàn)就怎么實(shí)現(xiàn)】

在編寫動(dòng)態(tài)代理之前塘砸,要明確幾個(gè)概念:

?代理對(duì)象擁有目標(biāo)對(duì)象相同的方法【因?yàn)閰?shù)二指定了對(duì)象的接口,代理對(duì)象會(huì)實(shí)現(xiàn)接口的所有方法】

?用戶調(diào)用代理對(duì)象的什么方法呜达,都是在調(diào)用處理器的invoke方法谣蠢∷诔埽【被攔截】

?使用JDK動(dòng)態(tài)代理必須要有接口【參數(shù)二需要接口】

上面也說(shuō)了:代理對(duì)象會(huì)實(shí)現(xiàn)接口的所有方法查近,這些實(shí)現(xiàn)的方法交由我們的handler來(lái)處理!

?所有通過(guò)動(dòng)態(tài)代理實(shí)現(xiàn)的方法全部通過(guò)invoke()調(diào)用

invoke()方法調(diào)用

所以動(dòng)態(tài)代理調(diào)用過(guò)程是這樣子的:
動(dòng)態(tài)代理調(diào)用過(guò)程示意圖

3.2靜態(tài)代理和動(dòng)態(tài)代理的區(qū)別


很明顯的是:

?靜態(tài)代理需要自己寫代理類-->代理類需要實(shí)現(xiàn)與目標(biāo)對(duì)象相同的接口

?而動(dòng)態(tài)代理不需要自己編寫代理類--->(是動(dòng)態(tài)生成的)

使用靜態(tài)代理時(shí):

?如果目標(biāo)對(duì)象的接口有很多方法的話挤忙,那我們還是得一一實(shí)現(xiàn)霜威,這樣就會(huì)比較麻煩

使用動(dòng)態(tài)代理時(shí):

?代理對(duì)象的生成,是利用JDKAPI册烈,動(dòng)態(tài)地在內(nèi)存中構(gòu)建代理對(duì)象(需要我們指定創(chuàng)建 代理對(duì)象/目標(biāo)對(duì)象 實(shí)現(xiàn)的接口的類型)戈泼,并且會(huì)默認(rèn)實(shí)現(xiàn)接口的全部方法婿禽。

四、典型應(yīng)用

我們之前寫中文過(guò)濾器的時(shí)候大猛,需要使用包裝設(shè)計(jì)模式來(lái)設(shè)計(jì)一個(gè)request類扭倾。如果不是Servlet提供了實(shí)現(xiàn)類給我們,我們使用包裝設(shè)計(jì)模式會(huì)比較麻煩

現(xiàn)在我們學(xué)習(xí)了動(dòng)態(tài)代理了挽绩,動(dòng)態(tài)代理就是攔截直接訪問(wèn)對(duì)象膛壹,可以給對(duì)象進(jìn)行增強(qiáng)的一項(xiàng)技能。

4.1中文過(guò)濾器

   public void doFilter(final ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        final HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        response.setContentType("text/html;charset=UTF-8");
        request.setCharacterEncoding("UTF-8");


        //放出去的是代理對(duì)象
        chain.doFilter((ServletRequest) Proxy.newProxyInstance(CharacterEncodingFilter.class.getClassLoader(), request.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                //判斷是不是getParameter方法
                if (!method.getName().equals("getParameter")) {

                    //不是就使用request調(diào)用
                   return method.invoke(request, args);
                }

                //判斷是否是get類型的
                if (!request.getMethod().equalsIgnoreCase("get")) {
                   return method.invoke(request, args);
                }

                //執(zhí)行到這里唉堪,只能是get類型的getParameter方法了模聋。
                String value = (String) method.invoke(request, args);
                if (value == null) {
                    return null;
                }
                return new String(value.getBytes("ISO8859-1"), "UTF-8");
            }

        }), response);

    }

五、總結(jié)

本文主要講解了代理模式的幾個(gè)要點(diǎn)唠亚,其實(shí)還有一些細(xì)節(jié)的:比如“強(qiáng)制代理”(只能通過(guò)被代理對(duì)象找到代理對(duì)象链方,不能繞過(guò)代理對(duì)象直接訪問(wèn)被代理對(duì)象)。只是用得比較少灶搜,我就不說(shuō)了~~

要實(shí)現(xiàn)動(dòng)態(tài)代理必須要有接口的祟蚀,動(dòng)態(tài)代理是基于接口來(lái)代理的(實(shí)現(xiàn)接口的所有方法),如果沒有接口的話我們可以考慮cglib代理占调。

cglib代理也叫子類代理暂题,從內(nèi)存中構(gòu)建出一個(gè)子類來(lái)擴(kuò)展目標(biāo)對(duì)象的功能

這里我就不再貼出代碼來(lái)了究珊,因?yàn)閏glib的代理教程也很多薪者,與動(dòng)態(tài)代理實(shí)現(xiàn)差不多~~~

總的來(lái)說(shuō):代理模式是我們寫代碼中用得很多的一種模式了,Spring的AOP底層其實(shí)就是動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)的-->面向切面編程剿涮。具體可參考我之前寫的那篇文章:
其實(shí)只要記住一點(diǎn):原有的對(duì)象需要額外的功能言津,想想動(dòng)態(tài)代理這項(xiàng)技術(shù)!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末取试,一起剝皮案震驚了整個(gè)濱河市悬槽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瞬浓,老刑警劉巖初婆,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異猿棉,居然都是意外死亡磅叛,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門萨赁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)弊琴,“玉大人,你說(shuō)我怎么就攤上這事杖爽∏枚” “怎么了紫皇?”我有些...
    開封第一講書人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)腋寨。 經(jīng)常有香客問(wèn)我聪铺,道長(zhǎng),這世上最難降的妖魔是什么萄窜? 我笑而不...
    開封第一講書人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任计寇,我火速辦了婚禮,結(jié)果婚禮上脂倦,老公的妹妹穿的比我還像新娘番宁。我一直安慰自己,他們只是感情好赖阻,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開白布蝶押。 她就那樣靜靜地躺著,像睡著了一般火欧。 火紅的嫁衣襯著肌膚如雪棋电。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,736評(píng)論 1 312
  • 那天苇侵,我揣著相機(jī)與錄音赶盔,去河邊找鬼。 笑死榆浓,一個(gè)胖子當(dāng)著我的面吹牛于未,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播陡鹃,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼烘浦,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了萍鲸?” 一聲冷哼從身側(cè)響起闷叉,我...
    開封第一講書人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎脊阴,沒想到半個(gè)月后握侧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嘿期,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年品擎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秽五。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡孽查,死狀恐怖饥悴,靈堂內(nèi)的尸體忽然破棺而出坦喘,到底是詐尸還是另有隱情盲再,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布瓣铣,位于F島的核電站答朋,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏棠笑。R本人自食惡果不足惜梦碗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蓖救。 院中可真熱鬧洪规,春花似錦、人聲如沸循捺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)从橘。三九已至念赶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間恰力,已是汗流浹背叉谜。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留踩萎,地道東北人停局。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像香府,于是被迫代替她去往敵國(guó)和親翻具。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

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