亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

java | 什么是動(dòng)態(tài)代理?

fxp / 3279人閱讀

摘要:代理模式在我們?nèi)粘V泻艹R?jiàn),生活處處有代理看張學(xué)友的演唱會(huì)很難搶票,可以找黃牛排隊(duì)買嫌出去吃飯麻煩,可以叫外賣無(wú)論是黃牛外賣騎手都得幫我們干活。靜態(tài)代理我還是以找黃牛幫我排隊(duì)買張學(xué)友的演唱會(huì)門票的例子,寫個(gè)說(shuō)明。

微信公眾號(hào):一個(gè)優(yōu)秀的廢人。如有問(wèn)題,請(qǐng)后臺(tái)留言,反正我也不會(huì)聽(tīng)。

最近在復(fù)習(xí) Java 相關(guān),回顧了下代理模式。代理模式在 Java 領(lǐng)域很多地方都有應(yīng)用,它分為靜態(tài)代理和動(dòng)態(tài)代理,其中 Spring AOP 就是動(dòng)態(tài)代理的典型例子。動(dòng)態(tài)代理又分為接口代理和 cglib (子類代理),結(jié)合我的理解寫了幾個(gè) demo 分享給你們,這是昨晚修仙到 3 點(diǎn)寫出來(lái)的文章,不點(diǎn)在看,我覺(jué)得說(shuō)不過(guò)去了。

代理模式在我們?nèi)粘V泻艹R?jiàn),生活處處有代理:

看張學(xué)友的演唱會(huì)很難搶票,可以找黃牛排隊(duì)買

嫌出去吃飯麻煩,可以叫外賣

無(wú)論是黃牛、外賣騎手都得幫我們干活。但是他們不能一手包辦(比如黃牛不能幫我吃飯),他們只能做我們不能或者不想做的事。

找黃??梢詭臀遗抨?duì)買上張學(xué)友的演唱會(huì)門票

外賣騎手可以幫我把飯送到樓下

所以,你看。代理模式其實(shí)就是當(dāng)前對(duì)象不愿意做的事情,委托給別的對(duì)象做。

靜態(tài)代理

我還是以找黃牛幫我排隊(duì)買張學(xué)友的演唱會(huì)門票的例子,寫個(gè) demo 說(shuō)明?,F(xiàn)在有一個(gè) Human 接口,無(wú)論是我還是黃牛都實(shí)現(xiàn)了這個(gè)接口。

public interface Human {

    void eat();

    void sleep();

    void lookConcert();

}

例如,我這個(gè)類,我會(huì)吃飯和睡覺(jué),如以下類:

public class Me implements Human{

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

    @Override
    public void sleep() {
        System.out.println("Go to bed at one o"clock in the morning");
    }

    @Override
    public void lookConcert() {
        System.out.println("Listen to Jacky Cheung"s Concert");
    }

}

有黃牛類,例如:

public class Me implements Human{

    @Override
    public void eat() {
    }

    @Override
    public void sleep() {
    }

    @Override
    public void lookConcert() {
    }

}

現(xiàn)在我和黃牛都已經(jīng)準(zhǔn)備好了,怎么把這二者關(guān)聯(lián)起來(lái)呢?我們要明確的是黃牛是要幫我買票的,買票必然就需要幫我排隊(duì),于是有以下黃牛類:注意這里我們不關(guān)心,黃牛的其他行為,我們只關(guān)心他能不能排隊(duì)買票。

public class HuangNiu implements Human{

    private Me me;

    public HuangNiu() {
        me = new Me();
    }

    @Override
    public void eat() {
    }

    @Override
    public void sleep() {
    }

    @Override
    public void lookConcert() {
        // 添加排隊(duì)買票方法
        this.lineUp();
        me.lookConcert();
    }

    public void lineUp() {

        System.out.println("line up");

    }

}

最終的 main 方法調(diào)用如下:

public class Client {

    public static void main(String[] args) {

        Human human = new HuangNiu();
        human.lookConcert();

    }

}

結(jié)果如下:

由此可見(jiàn),黃牛就只是做了我們不愿意做的事(排隊(duì)買票),實(shí)際看演唱會(huì)的人還是我??蛻舳艘膊⒉魂P(guān)心代理類代理了哪個(gè)類,因?yàn)榇a控制了客戶端對(duì)委托類的訪問(wèn)。客戶端代碼表現(xiàn)為 Human human = new HuangNiu();

由于代理類實(shí)現(xiàn)了抽象角色的接口,導(dǎo)致代理類無(wú)法通用。比如,我的狗病了,想去看醫(yī)生,但是排隊(duì)掛號(hào)很麻煩,我也想有個(gè)黃牛幫我的排隊(duì)掛號(hào)看病,但是黃牛它不懂這只狗的特性(黃牛跟狗不是同一類型,黃牛屬于 Human 但狗屬于 Animal 類)但排隊(duì)掛號(hào)和排隊(duì)買票相對(duì)于黃牛來(lái)說(shuō)它兩就是一件事,這個(gè)方法是不變的,現(xiàn)場(chǎng)排隊(duì)。那我們能不能找一個(gè)代理說(shuō)既可以幫人排隊(duì)買票也可以幫狗排隊(duì)掛號(hào)呢?

答案肯定是可以的,可以用動(dòng)態(tài)代理。

基于接口的動(dòng)態(tài)代理

如靜態(tài)代理的內(nèi)容所描述的,靜態(tài)代理受限于接口的實(shí)現(xiàn)。動(dòng)態(tài)代理就是通過(guò)使用反射,動(dòng)態(tài)地獲取抽象接口的類型,從而獲取相關(guān)特性進(jìn)行代理。因動(dòng)態(tài)代理能夠?yàn)樗械奈蟹竭M(jìn)行代理,因此給代理類起個(gè)通用點(diǎn)的名字 HuangNiuHandle。先看黃牛類可以變成什么樣?

public class HuangNiuHandle implements InvocationHandler {

    private Object proxyTarget;

    public Object getProxyInstance(Object target) {
        this.proxyTarget = target;
        return Proxy.newProxyInstance(proxyTarget.getClass().getClassLoader(), proxyTarget.getClass().getInterfaces(), this);
    }

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

        Object methodObject = null;

        System.out.println("line up");
        methodObject = method.invoke(proxyTarget, args);
        System.out.println("go home and sleep");

        return methodObject;
    }

}

這個(gè)時(shí)候的客戶端代碼就變成這樣了

public class Client {

    public static void main(String[] args) {

        HuangNiuHandle huangNiuHandle = new HuangNiuHandle();
        Human human = (Human) huangNiuHandle.getProxyInstance(new Me());

        human.eat();
        human.run();
        human.lookConcert();

        System.out.println("------------------");

        Animal animal = (Animal) huangNiuHandle.getProxyInstance(new Dog());
        animal.eat();
        animal.run();
        animal.seeADoctor();
    }

}

使用動(dòng)態(tài)代理有三個(gè)要點(diǎn),

必須實(shí)現(xiàn) InvocationHandler 接口,表明該類是一個(gè)動(dòng)態(tài)代理執(zhí)行類。

InvocationHandler 接口內(nèi)有一實(shí)現(xiàn)方法如下: public Object invoke(Object proxy, Method method, Object[] args) 。使用時(shí)需要重寫這個(gè)方法

獲取代理類,需要使用 Proxy.newProxyInstance(Clas loader, Class[] interfaces, InvocationHandler h) 這個(gè)方法去獲取Proxy對(duì)象(Proxy 類類型的實(shí)例)。

注意到 Proxy.newProxyInstance 這個(gè)方法,它需要傳入 3 個(gè)參數(shù)。解析如下:

// 第一個(gè)參數(shù),是類的加載器
// 第二個(gè)參數(shù)是委托類的接口類型,證代理類返回的是同一個(gè)實(shí)現(xiàn)接口下的類型,保持代理類與抽象角色行為的一致
// 第三個(gè)參數(shù)就是代理類本身,即告訴代理類,代理類遇到某個(gè)委托類的方法時(shí)該調(diào)用哪個(gè)類下的invoke方法
Proxy.newProxyInstance(Class loader, Class[] interfaces, InvocationHandler h)

再來(lái)看看 invoke 方法,用戶調(diào)用代理對(duì)象的什么方法,實(shí)質(zhì)上都是在調(diào)用處理器的
invoke 方法,通過(guò)該方法調(diào)用目標(biāo)方法,它也有三個(gè)參數(shù):

// 第一個(gè)參數(shù)為 Proxy 類類型實(shí)例,如匿名的 $proxy 實(shí)例
// 第二個(gè)參數(shù)為委托類的方法對(duì)象
// 第三個(gè)參數(shù)為委托類的方法參數(shù)
// 返回類型為委托類某個(gè)方法的執(zhí)行結(jié)果
public Object invoke(Object proxy, Method method, Object[] args)

調(diào)用該代理類之后的輸出結(jié)果:

由結(jié)果可知,黃牛不僅幫了(代理)我排隊(duì)買票,還幫了(代理)我的狗排隊(duì)掛號(hào)。所以,你看靜態(tài)代理需要自己寫代理類(代理類需要實(shí)現(xiàn)與目標(biāo)對(duì)象相同的接口),還需要一一實(shí)現(xiàn)接口方法,但動(dòng)態(tài)代理不需要。

注意,我們并不是所有的方法都需要黃牛這個(gè)代理去排隊(duì)。我們知道只有我看演唱會(huì)和我的狗去看醫(yī)生時(shí),才需要黃牛,如果要實(shí)現(xiàn)我們想要的方法上面添加特定的代理,可以通過(guò) invoke 方法里面的方法反射獲取 method 對(duì)象方法名稱即可實(shí)現(xiàn),所以動(dòng)態(tài)代理類可以變成這樣:

public class HuangNiuHandle implements InvocationHandler {

    private Object proxyTarget;

    public Object getProxyInstance(Object target) {
        this.proxyTarget = target;
        return Proxy.newProxyInstance(proxyTarget.getClass().getClassLoader(), proxyTarget.getClass().getInterfaces(), this);
    }

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

        Object methodObject = null;

        if ("lookConcert".equals(method.getName()) ||
        "seeADoctor".equals(method.getName())) {

            System.out.println("line up");
            // 調(diào)用目標(biāo)方法
            methodObject = method.invoke(proxyTarget, args);
        } else {
            // 不使用第一個(gè)proxy參數(shù)作為參數(shù),否則會(huì)造成死循環(huán)
            methodObject = method.invoke(proxyTarget, args);
        }

        return methodObject;
    }

}

結(jié)果如下:可以看到我們只在特定方法求助了黃牛

由此可見(jiàn),動(dòng)態(tài)代理一般應(yīng)用在記錄日志等橫向業(yè)務(wù)。

值得注意的是:

基于接口類的動(dòng)態(tài)代理模式,必須具備抽象角色、委托類、代理三個(gè)基本角色。委托類和代理類必須由抽象角色衍生出來(lái),否則無(wú)法使用該模式。

動(dòng)態(tài)代理模式最后返回的是具有抽象角色(頂層接口)的對(duì)象。在委托類內(nèi)被 private 或者 protected 關(guān)鍵修飾的方法將不會(huì)予以調(diào)用,即使允許調(diào)用。也無(wú)法在客戶端使用代理類轉(zhuǎn)換成子類接口,對(duì)方法進(jìn)行調(diào)用。也就是說(shuō)上述的動(dòng)態(tài)代理返回的是委托類(Me)或 (Dog)的就接口對(duì)象 (Human)或 (Animal)。

在 invoke 方法內(nèi)為什么不使用第一個(gè)參數(shù)進(jìn)行執(zhí)行回調(diào)。在客戶端使用getProxyInstance(new Child( ))時(shí),JDK 會(huì)返回一個(gè) proxy 的實(shí)例,實(shí)例內(nèi)有InvokecationHandler 對(duì)象及動(dòng)態(tài)繼承下來(lái)的目標(biāo) ??蛻舳苏{(diào)用了目標(biāo)方法,有如下操作:首先 JDK 先查找 proxy 實(shí)例內(nèi)的 handler 對(duì)象 然后執(zhí)行 handler 內(nèi)的 invoke 方法。

根據(jù) public Object invoke 這個(gè)方法第一個(gè)參數(shù) proxy 就是對(duì)應(yīng)著 proxy 實(shí)例。如果在 invoke 內(nèi)使用 method.invoke(proxy,args) ,會(huì)出現(xiàn)這樣一條方法鏈,目標(biāo)方法→invoke→目標(biāo)方法→invoke...,最終導(dǎo)致堆棧溢出。

基于子類的動(dòng)態(tài)代理

為了省事,我這里并沒(méi)有繼承父類,但在實(shí)際開(kāi)發(fā)中是需要繼承父類才比較方便擴(kuò)展的。與基于接口實(shí)現(xiàn)類不同的是:

CGLib (基于子類的動(dòng)態(tài)代理)使用的是方法攔截器 MethodInterceptor ,需要導(dǎo)入 cglib.jar 和 asm.jar 包

基于子類的動(dòng)態(tài)代理,返回的是子類對(duì)象

方法攔截器對(duì) protected 修飾的方法可以進(jìn)行調(diào)用

代碼如下:

public class Me {

    public void eat() {
        System.out.println("eat meat ....");
    }

    public void run() {
        System.out.println("I run with two legs");
    }

    public void lookConcert() {
        System.out.println("Listen to Jacky Cheung"s Concert");
    }

    protected void sleep() {
        System.out.println("Go to bed at one o"clock in the morning");
    }

}

Dog 類

public class Dog {

    public void eat() {
        System.out.println("eat Dog food ....");
    }

    public void run() {
        System.out.println("Dog running with four legs");
    }

    public void seeADoctor() {
        System.out.println("The dog go to the hospital");
    }

}

黃牛代理類,注意 invoke() 這里多了一個(gè)參數(shù) methodProxy ,它的作用是用于執(zhí)行目標(biāo)(委托類)的方法,至于為什么用 methodProxy ,官方的解釋是速度快且在intercep t內(nèi)調(diào)用委托類方法時(shí)不用保存委托對(duì)象引用。

public class HuangNiuHandle implements MethodInterceptor {

    private Object proxyTarget;

    public Object getProxyInstance(Object target) {
        this.proxyTarget = target;
        return Enhancer.create(target.getClass(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

        Object methodObject = null;

        if ("lookConcert".equals(method.getName()) ||
                "seeADoctor".equals(method.getName())) {
            System.out.println("line up");
            // 調(diào)用目標(biāo)方法
            methodObject = methodProxy.invokeSuper(proxy, args);
        } else {
            methodObject = method.invoke(proxyTarget, args);
        }

        return methodObject;
    }
}

client 類

public class Client {

    public static void main(String[] args) {
        HuangNiuHandle huangNiuHandle = new HuangNiuHandle();
        Me me = (Me) huangNiuHandle.getProxyInstance(new Me());

        me.eat();
        me.run();
        me.sleep();
        me.lookConcert();

        System.out.println("------------------");

        Dog dog = (Dog) huangNiuHandle.getProxyInstance(new Dog());
        dog.eat();
        dog.run();
        dog.seeADoctor();
    }
}

結(jié)果:

注意到 Me 類中被 protected 修飾的方法 sleep 仍然可以被客戶端調(diào)用。這在基于接口的動(dòng)態(tài)代理中是不被允許的。

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

靜態(tài)代理需要自己寫代理類并一一實(shí)現(xiàn)目標(biāo)方法,且代理類必須實(shí)現(xiàn)與目標(biāo)對(duì)象相同的接口。

動(dòng)態(tài)代理不需要自己實(shí)現(xiàn)代理類,它是利用 JDKAPI,動(dòng)態(tài)地在內(nèi)存中構(gòu)建代理對(duì)象(需要我們傳入被代理類),并且默認(rèn)實(shí)現(xiàn)所有目標(biāo)方法。

源碼下載:https://github.com/turoDog/re...

后語(yǔ)

如果本文對(duì)你哪怕有一丁點(diǎn)幫助,請(qǐng)幫忙點(diǎn)好看,你的好看是我堅(jiān)持寫作的動(dòng)力。關(guān)注公眾號(hào)一個(gè)優(yōu)秀的廢人回復(fù) 1024 獲取資料:Python、C++、Java、Linux、Go、前端、算法資料分享

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/77643.html

相關(guān)文章

  • 你真的完全了解Java動(dòng)態(tài)代理嗎?看這篇就夠了

    摘要:動(dòng)態(tài)地代理,可以猜測(cè)一下它的含義,在運(yùn)行時(shí)動(dòng)態(tài)地對(duì)某些東西代理,代理它做了其他事情。所以動(dòng)態(tài)代理的內(nèi)容重點(diǎn)就是這個(gè)。所以下一篇我們來(lái)細(xì)致了解下的到底是怎么使用動(dòng)態(tài)代理的。 之前講了《零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)》,本來(lái)打算下一篇講講Srping的AOP的,但是其中會(huì)涉及到Java的動(dòng)態(tài)代理,所以先單獨(dú)一篇來(lái)了解下Java的動(dòng)態(tài)代理到底是什么,Java是怎么實(shí)現(xiàn)它的。 ...

    haitiancoder 評(píng)論0 收藏0
  • 給女朋友講解什么代理模式

    摘要:受知乎文章和設(shè)計(jì)模式之禪的啟發(fā),我也來(lái)搞一篇腦洞小開(kāi)的文章由標(biāo)題可知,這篇文章是寫給我女朋友看的。于是這就讓經(jīng)紀(jì)人對(duì)粉絲說(shuō)只有萬(wàn),我才會(huì)寫代碼。 前言 只有光頭才能變強(qiáng) 回顧前面: ThreadLocal就是這么簡(jiǎn)單 多線程三分鐘就可以入個(gè)門了! 多線程基礎(chǔ)必要知識(shí)點(diǎn)!看了學(xué)習(xí)多線程事半功倍 Java鎖機(jī)制了解一下 AQS簡(jiǎn)簡(jiǎn)單單過(guò)一遍 Lock鎖子類了解一下 線程池你真不來(lái)了解一下...

    stormgens 評(píng)論0 收藏0
  • java動(dòng)態(tài)代理及RPC框架介紹

    摘要:這種語(yǔ)法,在中被稱為動(dòng)態(tài)代理。在動(dòng)態(tài)代理機(jī)制中,這個(gè)角色只能是接口。動(dòng)態(tài)代理就是實(shí)現(xiàn)的技術(shù)之一。 所謂動(dòng)態(tài)代理,指的是語(yǔ)言提供的一種語(yǔ)法,能夠?qū)?duì)對(duì)象中不同方法的調(diào)用重定向到一個(gè)統(tǒng)一的處理函數(shù)中來(lái)。python重寫__getattr__函數(shù)能夠做到這一點(diǎn),就連世界上最好的語(yǔ)言也提供稱為魔術(shù)方法的__call。這種語(yǔ)法除了能更好的實(shí)現(xiàn)動(dòng)態(tài)代理外,還是RPC框架實(shí)現(xiàn)原理的一部分。 動(dòng)態(tài)代理...

    2shou 評(píng)論0 收藏0
  • Java動(dòng)態(tài)代理 jdk和cglib的實(shí)現(xiàn)比較

    摘要:與靜態(tài)代理對(duì)比,動(dòng)態(tài)代理是在動(dòng)態(tài)生成代理類,由代理類完成對(duì)具體方法的封裝,實(shí)現(xiàn)的功能。本文將分析中兩種動(dòng)態(tài)代理的實(shí)現(xiàn)方式,和,比較它們的異同。那如何動(dòng)態(tài)編譯呢你可以使用,這是一個(gè)封裝了的庫(kù),幫助你方便地實(shí)現(xiàn)動(dòng)態(tài)編譯源代碼。 發(fā)現(xiàn)Java面試很喜歡問(wèn)Spring AOP怎么實(shí)現(xiàn)的之類的問(wèn)題,所以寫一篇文章來(lái)整理一下。關(guān)于AOP和代理模式的概念這里并不做贅述,而是直奔主題,即AOP的實(shí)現(xiàn)方...

    h9911 評(píng)論0 收藏0
  • Java 動(dòng)態(tài)代理(Dynamic proxy) 小結(jié)

    摘要:代理模式基本概念不論是靜態(tài)代理還是動(dòng)態(tài)代理其本質(zhì)都是代理模式的一種實(shí)現(xiàn)那么什么是代理模式呢代理模式即給某一個(gè)對(duì)象提供一個(gè)代理并由代理對(duì)象控制對(duì)原對(duì)象的引用代理模式其實(shí)取材于實(shí)際生活例如我們生活中常見(jiàn)的房屋租賃代理我們?cè)谧夥繒r(shí)一般不是直接和房 代理模式 基本概念 不論是靜態(tài)代理還是動(dòng)態(tài)代理, 其本質(zhì)都是代理模式的一種實(shí)現(xiàn), 那么什么是代理模式呢?代理模式, 即給某一個(gè)對(duì)象提供一個(gè)代理, ...

    Jason 評(píng)論0 收藏0
  • Java動(dòng)態(tài)代理深度解析

    摘要:動(dòng)態(tài)代理深度解析引言說(shuō)起動(dòng)態(tài)代理,很多人可能都沒(méi)有直接去使用過(guò)。因?yàn)榈膭?dòng)態(tài)代理只能代理接口,而不能代理原始的類。接下來(lái)是真正壓軸的環(huán)節(jié),實(shí)現(xiàn)自己的動(dòng)態(tài)代理類。 Java動(dòng)態(tài)代理深度解析 引言 說(shuō)起動(dòng)態(tài)代理,很多人可能都沒(méi)有直接去使用過(guò)。但是只要用過(guò)Spring,那動(dòng)態(tài)代理就是一個(gè)是個(gè)繞不過(guò)的坎,因?yàn)镾pring的核心特性之一AOP就是基于動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)的,那么什么情況下需要用到動(dòng)態(tài)代理...

    whinc 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<