成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JDK動態(tài)代理的理解與分析

stormjun / 3007人閱讀

摘要:類所實(shí)現(xiàn)的方法包裝了對被代理對象的反射調(diào)用,后文中的動態(tài)代理類正是調(diào)用此方法來調(diào)用被代理對象的方法。

前言

java的設(shè)計(jì)模式中有一項(xiàng)設(shè)計(jì)模式叫做代理模式,所謂代理模式,就是通過代理方來操作目標(biāo)對象,而不是自己直接調(diào)用。代理又分為靜態(tài)代理和動態(tài)代理,靜態(tài)代理就是針對每個被代理對象寫一個代理類,操作不夠優(yōu)雅;動態(tài)代理,可以根據(jù)接口動態(tài)的生成代理類,這動態(tài)生成的類不需要自己書寫,jdk幫你完成了。無論是動態(tài)代理還是靜態(tài)代理,最終都會產(chǎn)生一個代理類(class文件),里面都含有對被代理對象的封裝,只是誕生的途徑不一樣。下面我在代碼層面詳細(xì)介紹一下這兩種代理的實(shí)現(xiàn)和原理。

本文來自于我的博客網(wǎng)站http://51think.net,歡迎來訪。

一、靜態(tài)代理

1、創(chuàng)建手機(jī)接口 ,擁有打電話的行為

public interface MobilePhone {
    //打電話給jack
    void callJack();
}

2、創(chuàng)建實(shí)現(xiàn)類安卓手機(jī),實(shí)現(xiàn)此接口

public class AndroidMobilePhone implements MobilePhone{
     private String name;
     private String age;

    public AndroidMobilePhone(String name, String age) {
        this.name = name;
        this.age = age;
    }
    //打電話給jack
    @Override
    public void callJack(){
         System.out.println(" hey boy! name="+name+",age="+age);
    }
}

3、創(chuàng)建靜態(tài)代理類,實(shí)現(xiàn)此接口

public class AndroidMobileStaticProxyPhone implements MobilePhone{
     private MobilePhone amp;

    public AndroidMobileStaticProxyPhone(MobilePhone amp) {
        this.amp = amp;
    }
    //打電話給jack
    @Override
    public void callJack(){
        System.out.println("--靜態(tài)代理前置--");
        amp.callJack();
        System.out.println("--靜態(tài)代理后置--");
    }
}

從靜態(tài)代理類AndroidMobileStaticProxyPhone 中,我們可以發(fā)現(xiàn),他持有了MobilePhone 類型的對象,一旦將被代理對象傳入,它就可以操作被代理對象了。

4、創(chuàng)建main方法調(diào)用

如果我們不使用代理,調(diào)用是這樣的:

MobilePhone mp=new AndroidMobilePhone("杰克","23");
mp..callJack();

如果使用靜態(tài)代理,調(diào)用變成如下方式:

 MobilePhone mp=new AndroidMobilePhone("杰克","23");
 MobilePhone staticProxy=new AndroidMobileStaticProxyPhone(mp);
 staticProxy.callJack();

從上述代碼中,我們可以看出,靜態(tài)代理其實(shí)就是通過一個包裝類來調(diào)用目標(biāo)對象而已。

二、動態(tài)代理

1、仍然沿用MobilePhone接口類
2、創(chuàng)建java.lang.reflect.InvocationHandler接口的實(shí)現(xiàn)類MobilePhoneHandler

public class MobilePhoneHandler implements InvocationHandler {
    private T target;

    public MobilePhoneHandler(T target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //前置處理
        System.out.println("--動態(tài)代理前置處理--");
        Object obj=method.invoke(target,args);
        //后置處理
        System.out.println("--動態(tài)代理后置處理--");
        return obj;
    }
}

關(guān)于InvocationHandler ,源碼注釋如下:

* 

Each proxy instance has an associated invocation handler. * When a method is invoked on a proxy instance, the method * invocation is encoded and dispatched to the {@code invoke} * method of its invocation handler.

即,每個代理實(shí)例都需要關(guān)聯(lián)一個invocation handler,當(dāng)一個方法被代理實(shí)例調(diào)用時,這個方法會被編碼并發(fā)送到invocation handler中進(jìn)行處理。這里所說的invocation handler即本文中剛剛創(chuàng)建的 MobilePhoneHandler 類。MobilePhoneHandler類所實(shí)現(xiàn)的invoke方法包裝了對被代理對象的反射調(diào)用,后文中的動態(tài)代理類正是調(diào)用此invoke方法來調(diào)用被代理對象的方法。

3、創(chuàng)建main方法調(diào)用

MobilePhone mp=new AndroidMobilePhone("杰克","23");
InvocationHandler handler=new MobilePhoneHandler(mp);
MobilePhone mpProxy=(MobilePhone)Proxy.newProxyInstance(MobilePhone.class.getClassLoader(),new Class[]{MobilePhone.class},handler );
mpProxy.callJack();

輸出如下:

--動態(tài)代理前置處理--
 hey boy! name=杰克,age=23
--動態(tài)代理后置處理--

在輸出內(nèi)容的前置處理和后置處理中,我們可以加一些橫向的處理邏輯,這樣就變成了spring 的AOP。

關(guān)注Proxy.newProxyInstance這個方法調(diào)用,同樣是來自于java.lang.reflect包里的類。看一下源碼注釋:

     * Returns an instance of a proxy class for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.  This method is equivalent to:
     * 
     *     Proxy.getProxyClass(loader, interfaces).
     *         getConstructor(new Class[] { InvocationHandler.class }).
     *         newInstance(new Object[] { handler });
     * 
*

注釋中表明,這個newProxyInstance方法返回了一個特定接口代理類的實(shí)例,這個代理實(shí)例將方法調(diào)用分配給特定的invocation handler。這個Proxy.newProxyInstance方法等同于如下調(diào)用:

 Proxy.getProxyClass(loader, interfaces). getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler });

我們用debug方式跟蹤一下代碼,newProxyInstance方法最終會執(zhí)行到Proxy的內(nèi)部類ProxyClassFactory的apply方法:

long num = nextUniqueNumber.getAndIncrement();這一行使用cas生成一個自增長的序號 。
關(guān)注ProxyGenerator.generateProxyClass 方法:

此方法動態(tài)生成一個class文件,這個class文件就是我們所說的動態(tài)代理類! 我們用代碼的方式將這個class文件寫出來:

 byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", AndroidMobilePhone.class.getInterfaces());
        String path = "E:projectspaceTestincomproxyMobileProxy.class";
        try(FileOutputStream fos = new FileOutputStream(path)) {
            fos.write(classFile);
            fos.flush();
            System.out.println("代理類class文件寫入成功");
        } catch (Exception e) {
            System.out.println("寫文件錯誤");
        }

到目錄中找到此class文件:

反編先看一下反編譯的類名和實(shí)現(xiàn)關(guān)系:

從此圖中可以看出,動態(tài)代理類最終還是實(shí)現(xiàn)了我們的MobilePhone接口,即動態(tài)代理類也是MobilePhone接口的一個實(shí)現(xiàn)類,它也實(shí)現(xiàn)了callJack方法。如下:

紅框標(biāo)注this.h.invoke(this, m3, null);中的h正是我們上文中創(chuàng)建的MobilePhoneHandler類的對象。這樣即可完成對被代理對象的調(diào)用。類的調(diào)用關(guān)系如下:

總結(jié)

我們再看一下之前main方法中的這一行:

MobilePhone mpProxy=(MobilePhone)Proxy.newProxyInstance(MobilePhone.class.getClassLoader(),new Class[]{MobilePhone.class},handler );

現(xiàn)在可以得知Proxy.newProxyInstance返回的是動態(tài)生成的代理類$Proxy0的對象,也可以稱作是MobilePhone 接口的一個實(shí)現(xiàn)類的對象。當(dāng)調(diào)用mpProxy.callJack()時,其實(shí)是調(diào)用$Proxy0.callJack(),然后對照剛剛的類調(diào)用關(guān)系圖,即可調(diào)用到被代理對象AndroidMobilePhone實(shí)例的callJack方法,從而實(shí)現(xiàn)了動態(tài)代理。

當(dāng)我們具象的查看某一個動態(tài)代理class反編譯文件時,比如$Proxy0,它內(nèi)部就是采用靜態(tài)代理的方式進(jìn)行包裝。其動態(tài)是體現(xiàn)在,能夠在給定的接口和invocationHandler情況下,動態(tài)生成代理類,如$Proxy0,$Proxy1,$Proxy2等等,不必手動創(chuàng)建,使用起來更靈活。

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

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/74176.html

相關(guān)文章

  • 從源碼入手,一文帶你讀懂Spring AOP面向切面編程

    摘要:,,面向切面編程。,切點(diǎn),切面匹配連接點(diǎn)的點(diǎn),一般與切點(diǎn)表達(dá)式相關(guān),就是切面如何切點(diǎn)。例子中,注解就是切點(diǎn)表達(dá)式,匹配對應(yīng)的連接點(diǎn),通知,指在切面的某個特定的連接點(diǎn)上執(zhí)行的動作。,織入,將作用在的過程。因?yàn)樵创a都是英文寫的。 之前《零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)》詳細(xì)講了Spring容器的初始化和加載的原理,后面《你真的完全了解Java動態(tài)代理嗎?看這篇就夠了》介紹了下...

    wawor4827 評論0 收藏0
  • Java 代理模式 AOP

    摘要:本文首發(fā)于作者最近在學(xué),研究了下和代理模式,寫點(diǎn)心得和大家分享下。所以下面來重點(diǎn)分析下代理模式。這里代理模式分為靜態(tài)代理和動態(tài)代理兩種,我們分別來看下。代理模式,代理,意味著有一方代替另一方完成一件事。 本文首發(fā)于 https://jaychen.cc作者 jaychen 最近在學(xué) Spring,研究了下 AOP 和代理模式,寫點(diǎn)心得和大家分享下。 AOP 先說下AOP,AOP 全稱 ...

    jk_v1 評論0 收藏0
  • Java動態(tài)代理 jdk和cglib實(shí)現(xiàn)比較

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

    h9911 評論0 收藏0
  • 學(xué)Aop?看這篇文章就夠了?。?!

    摘要:又是什么其實(shí)就是一種實(shí)現(xiàn)動態(tài)代理的技術(shù),利用了開源包,先將代理對象類的文件加載進(jìn)來,之后通過修改其字節(jié)碼并且生成子類。 在實(shí)際研發(fā)中,Spring是我們經(jīng)常會使用的框架,畢竟它們太火了,也因此Spring相關(guān)的知識點(diǎn)也是面試必問點(diǎn),今天我們就大話Aop。特地在周末推文,因?yàn)樵撈恼麻喿x起來還是比較輕松詼諧的,當(dāng)然了,更主要的是周末的我也在充電學(xué)習(xí),希望有追求的朋友們也盡量不要放過周末時...

    boredream 評論0 收藏0
  • Java 動態(tài)代理 理解

    摘要:之后通過類的靜態(tài)方法取得一個代理類實(shí)例再次鄙視自己。值得一提,動態(tài)代理把也代理了??偨Y(jié)動態(tài)代理優(yōu)點(diǎn)相比靜態(tài)代理,不用每代理一個類就得寫一個新的代理類。缺點(diǎn)只能代理實(shí)現(xiàn)了接口的類,因?yàn)槭菃卫^承,代理類已經(jīng)是類的子類了。 動態(tài)代理 這里暫時只做JDK動態(tài)代理分析。動態(tài)代理應(yīng)用廣泛,例如AOP。 showImg(https://segmentfault.com/img/bVUmAr?w=21...

    3fuyu 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<