1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Java中的原生動態(tài)代理和CGLIB動態(tài)代理的原理,我不信你全知道!

        共 7017字,需瀏覽 15分鐘

         ·

        2020-05-28 23:22

        作者CarpenterLee

        cnblogs.com/CarpenterLee/p/8241042.html

        動態(tài)代理在Java中有著廣泛的應用,比如Spring AOP,Hibernate數(shù)據(jù)查詢、測試框架的后端mock、RPC,Java注解對象獲取等。靜態(tài)代理的代理關系在編譯時就確定了,而動態(tài)代理的代理關系是在編譯期確定的。靜態(tài)代理實現(xiàn)簡單,適合于代理類較少且確定的情況,而動態(tài)代理則給我們提供了更大的靈活性。

        今天我們來探討Java中兩種常見的動態(tài)代理方式:JDK原生動態(tài)代理和CGLIB動態(tài)代理。

        JDK原生動態(tài)代理

        先從直觀的示例說起,假設我們有一個接口Hello和一個簡單實現(xiàn)HelloImp:

        //?接口
        interface?Hello{
        ????String?sayHello(String?str);
        }
        //?實現(xiàn)
        class?HelloImp?implements?Hello{
        ????@Override
        ????public?String?sayHello(String?str)?{
        ????????return?"HelloImp:?"?+?str;
        ????}
        }

        這是Java種再常見不過的場景,使用接口制定協(xié)議,然后用不同的實現(xiàn)來實現(xiàn)具體行為。假設你已經(jīng)拿到上述類庫,如果我們想通過日志記錄對sayHello()的調(diào)用,使用靜態(tài)代理可以這樣做:

        //?靜態(tài)代理方式
        class?StaticProxiedHello?implements?Hello{
        ????...
        ????private?Hello?hello?=?new?HelloImp();
        ????@Override
        ????public?String?sayHello(String?str)?{
        ????????logger.info("You?said:?"?+?str);
        ????????return?hello.sayHello(str);
        ????}
        }

        上例中靜態(tài)代理類StaticProxiedHello作為HelloImp的代理,實現(xiàn)了相同的Hello接口。

        用Java動態(tài)代理可以這樣做:

        • 首先實現(xiàn)一個InvocationHandler,方法調(diào)用會被轉發(fā)到該類的invoke()方法。

        • 然后在需要使用Hello的時候,通過JDK動態(tài)代理獲取Hello的代理對象。

        //?Java?Proxy
        // 1. 首先實現(xiàn)一個InvocationHandler,方法調(diào)用會被轉發(fā)到該類的invoke()方法。
        class?LogInvocationHandler?implements?InvocationHandler{
        ????...
        ????private?Hello?hello;
        ????public?LogInvocationHandler(Hello?hello)?{
        ????????this.hello?=?hello;
        ????}
        ????@Override
        ????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
        ????????if("sayHello".equals(method.getName()))?{
        ????????????logger.info("You?said:?"?+?Arrays.toString(args));
        ????????}
        ????????return?method.invoke(hello,?args);
        ????}
        }
        // 2. 然后在需要使用Hello的時候,通過JDK動態(tài)代理獲取Hello的代理對象。
        Hello?hello?=?(Hello)Proxy.newProxyInstance(
        ????getClass().getClassLoader(),?//?1.?類加載器
        ????new?Class[]?{Hello.class},?//?2.?代理需要實現(xiàn)的接口,可以有多個
        ????new?LogInvocationHandler(new?HelloImp()));//?3.?方法調(diào)用的實際處理者
        System.out.println(hello.sayHello("I?love?you!"));

        運行上述代碼輸出結果:

        日志信息:?You?said:?[I?love?you!]
        HelloImp:?I?love?you!

        上述代碼的關鍵是Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler handler)方法,該方法會根據(jù)指定的參數(shù)動態(tài)創(chuàng)建代理對象。三個參數(shù)的意義如下:

        • loader,指定代理對象的類加載器;

        • interfaces,代理對象需要實現(xiàn)的接口,可以同時指定多個接口;

        • handler,方法調(diào)用的實際處理者,代理對象的方法調(diào)用都會轉發(fā)到這里(*注意1)。

        newProxyInstance()會返回一個實現(xiàn)了指定接口的代理對象,對該對象的所有方法調(diào)用都會轉發(fā)給InvocationHandler.invoke()方法。理解上述代碼需要對Java反射機制有一定了解。動態(tài)代理神奇的地方就是:

        • 代理對象是在程序運行時產(chǎn)生的,而不是編譯期;

        • 對代理對象的所有接口方法調(diào)用都會轉發(fā)到InvocationHandler.invoke()方法,在invoke()方法里我們可以加入任何邏輯,比如修改方法參數(shù),加入日志功能、安全檢查功能等;之后我們通過某種方式執(zhí)行真正的方法體,示例中通過反射調(diào)用了Hello對象的相應方法,還可以通過RPC調(diào)用遠程方法。

        注意1:對于從Object中繼承的方法,JDK Proxy會把hashCode()、equals()、toString()這三個非接口方法轉發(fā)給InvocationHandler,其余的Object方法則不會轉發(fā)。詳見JDK Proxy官方文檔。

        如果對JDK代理后的對象類型進行深挖,可以看到如下信息:

        #?Hello代理對象的類型信息
        class=class?jdkproxy.$Proxy0
        superClass=class?java.lang.reflect.Proxy
        interfaces:?
        interface?jdkproxy.Hello
        invocationHandler=jdkproxy.LogInvocationHandler@a09ee92

        代理對象的類型是jdkproxy.$Proxy0,這是個動態(tài)生成的類型,類名是形如$ProxyN的形式;父類是java.lang.reflect.Proxy,所有的JDK動態(tài)代理都會繼承這個類;同時實現(xiàn)了Hello接口,也就是我們接口列表中指定的那些接口。擴展:Java面試題內(nèi)容聚合

        如果你還對jdkproxy.$Proxy0具體實現(xiàn)感興趣,它大致長這個樣子:

        //?JDK代理類具體實現(xiàn)
        public?final?class?$Proxy0?extends?Proxy?implements?Hello
        {
        ??...
        ??public?$Proxy0(InvocationHandler?invocationhandler)
        ??{
        ????super(invocationhandler);
        ??}
        ??...
        ??@Override
        ??public?final?String?sayHello(String?str){
        ????...
        ????return?super.h.invoke(this,?m3,?new?Object[]?{str});//?將方法調(diào)用轉發(fā)給invocationhandler
        ????...
        ??}
        ??...
        }

        這些邏輯沒什么復雜之處,但是他們是在運行時動態(tài)產(chǎn)生的,無需我們手動編寫。更多詳情,可參考:

        https://www.jianshu.com/p/e2917b0b9614

        Java動態(tài)代理為我們提供了非常靈活的代理機制,但Java動態(tài)代理是基于接口的,如果對象沒有實現(xiàn)接口我們該如何代理呢?CGLIB登場。

        CGLIB動態(tài)代理

        CGLIB(Code Generation Library)是一個基于ASM的字節(jié)碼生成庫,它允許我們在運行時對字節(jié)碼進行修改和動態(tài)生成。CGLIB通過繼承方式實現(xiàn)代理。

        來看示例,假設我們有一個沒有實現(xiàn)任何接口的類HelloConcrete:

        public?class?HelloConcrete?{
        ????public?String?sayHello(String?str)?{
        ????????return?"HelloConcrete:?"?+?str;
        ????}
        }

        因為沒有實現(xiàn)接口該類無法使用JDK代理,通過CGLIB代理實現(xiàn)如下:

        • 首先實現(xiàn)一個MethodInterceptor,方法調(diào)用會被轉發(fā)到該類的intercept()方法。

        • 然后在需要使用HelloConcrete的時候,通過CGLIB動態(tài)代理獲取代理對象。

        //?CGLIB動態(tài)代理
        // 1. 首先實現(xiàn)一個MethodInterceptor,方法調(diào)用會被轉發(fā)到該類的intercept()方法。
        class?MyMethodInterceptor?implements?MethodInterceptor{
        ??...
        ????@Override
        ????public?Object?intercept(Object?obj,?Method?method,?Object[]?args,?MethodProxy?proxy)?throws?Throwable?{
        ????????logger.info("You?said:?"?+?Arrays.toString(args));
        ????????return?proxy.invokeSuper(obj,?args);
        ????}
        }
        // 2. 然后在需要使用HelloConcrete的時候,通過CGLIB動態(tài)代理獲取代理對象。
        Enhancer?enhancer?=?new?Enhancer();
        enhancer.setSuperclass(HelloConcrete.class);
        enhancer.setCallback(new?MyMethodInterceptor());

        HelloConcrete?hello?=?(HelloConcrete)enhancer.create();
        System.out.println(hello.sayHello("I?love?you!"));

        運行上述代碼輸出結果:

        日志信息:?You?said:?[I?love?you!]
        HelloConcrete:?I?love?you!

        上述代碼中,我們通過CGLIB的Enhancer來指定要代理的目標對象、實際處理代理邏輯的對象,最終通過調(diào)用create()方法得到代理對象,對這個對象所有非final方法的調(diào)用都會轉發(fā)給MethodInterceptor.intercept()方法,在intercept()方法里我們可以加入任何邏輯,比如修改方法參數(shù),加入日志功能、安全檢查功能等;

        通過調(diào)用MethodProxy.invokeSuper()方法,我們將調(diào)用轉發(fā)給原始對象,具體到本例,就是HelloConcrete的具體方法。CGLIG中MethodInterceptor的作用跟JDK代理中的InvocationHandler很類似,都是方法調(diào)用的中轉站。

        注意:對于從Object中繼承的方法,CGLIB代理也會進行代理,如hashCode()、equals()、toString()等,但是getClass()、wait()等方法不會,因為它是final方法,CGLIB無法代理。

        如果對CGLIB代理之后的對象類型進行深挖,可以看到如下信息:

        #?HelloConcrete代理對象的類型信息
        class=class?cglib.HelloConcrete$$EnhancerByCGLIB$$e3734e52
        superClass=class?lh.HelloConcrete
        interfaces:?
        interface?net.sf.cglib.proxy.Factory
        invocationHandler=not?java?proxy?class

        我們看到使用CGLIB代理之后的對象類型是cglib.HelloConcrete$$EnhancerByCGLIB$$e3734e52,這是CGLIB動態(tài)生成的類型;父類是HelloConcrete,印證了CGLIB是通過繼承實現(xiàn)代理;同時實現(xiàn)了net.sf.cglib.proxy.Factory接口,這個接口是CGLIB自己加入的,包含一些工具方法。

        注意,既然是繼承就不得不考慮final的問題。我們知道final類型不能有子類,所以CGLIB不能代理final類型,遇到這種情況會拋出類似如下異常:

        java.lang.IllegalArgumentException: Cannot subclass final class cglib.HelloConcrete

        同樣的,final方法是不能重載的,所以也不能通過CGLIB代理,遇到這種情況不會拋異常,而是會跳過final方法只代理其他方法。

        如果你還對代理類cglib.HelloConcrete$$EnhancerByCGLIB$$e3734e52具體實現(xiàn)感興趣,它大致長這個樣子:

        //?CGLIB代理類具體實現(xiàn)
        public?class?HelloConcrete$$EnhancerByCGLIB$$e3734e52
        ??extends?HelloConcrete
        ??implements?Factory
        {
        ??...
        ??private?MethodInterceptor?CGLIB$CALLBACK_0;?//?~~
        ??...

        ??public?final?String?sayHello(String?paramString)
        ??
        {
        ????...
        ????MethodInterceptor?tmp17_14?=?CGLIB$CALLBACK_0;
        ????if?(tmp17_14?!=?null)?{
        ??????//?將請求轉發(fā)給MethodInterceptor.intercept()方法。
        ??????return?(String)tmp17_14.intercept(this,?
        ??????????????CGLIB$sayHello$0$Method,?
        ??????????????new?Object[]?{?paramString?},?
        ??????????????CGLIB$sayHello$0$Proxy);
        ????}
        ????return?super.sayHello(paramString);
        ??}
        ??...
        }

        上述代碼我們看到,當調(diào)用代理對象的sayHello()方法時,首先會嘗試轉發(fā)給MethodInterceptor.intercept()方法,如果沒有MethodInterceptor就執(zhí)行父類的sayHello()。這些邏輯沒什么復雜之處,但是他們是在運行時動態(tài)產(chǎn)生的,無需我們手動編寫。

        更多關于CGLIB的介紹可以參考:

        https://dzone.com/articles/cglib-missing-manual

        結語

        本文介紹了Java兩種常見動態(tài)代理機制的用法和原理,JDK原生動態(tài)代理是Java原生支持的,不需要任何外部依賴,但是它只能基于接口進行代理;CGLIB通過繼承的方式進行代理,無論目標對象有沒有實現(xiàn)接口都可以代理,但是無法處理final的情況。

        動態(tài)代理是Spring AOP(Aspect Orient Programming, 面向切面編程)的實現(xiàn)方式,了解動態(tài)代理原理,對理解Spring AOP大有幫助。

        dfa924983a33a85d0724959351bfe547.webp

        最近我一直在面試高級工程師,不管初級,高級,程序員,我想面試前,大家刷題一定是是少不了吧。

        我也一樣,我在網(wǎng)上找了很多面試題來看,最近又趕上跳槽的高峰期,好多粉絲,都問我要有沒有最新面試題,索性,我就把我看過的和我面試中的真題,及答案都整理好,整理了《第2版:互聯(lián)網(wǎng)大廠面試題》分類?65?PDF,累計 2340頁!我會持續(xù)更新中,馬上就出第三版,涵蓋大廠算法會更多!

        第2版:題庫非常全面

        包括 Java 集合、JVM、多線程、并發(fā)編程、設計模式、Spring全家桶、Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、MongoDB、Redis、MySQL、RabbitMQ、Kafka、Linux、Netty、Tomcat、Python、HTML、CSS、Vue、React、JavaScript、Android 大數(shù)據(jù)、阿里巴巴等大廠面試題等、等技術棧!

        第2版:都是親自整理,看看縮略圖吧

        2ea825fbc97e0787ce534254ea493001.webp


        我放在我的Java開發(fā)寶典里了,掃碼下方二維碼

        回復:111,即可下載



        點贊是最大的支持?1265b4861047942ec7aa53c6ca73f34e.webp

        瀏覽 25
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            97免费在线观看视频| 激情av在线| 国产一級A片免费看| 日本豆花视频| 亚洲天堂在线看| 色婷婷AV在线观看| 精品爆乳| 日本豆花视频| 国产精品码ls字幕影视| 看一级黄色片| 日逼国产| 国产成人无码区免费视频| 99久久人妻精品免费二区| 大香蕉99热| 成人A片在线| 欧美丰满美乳XXⅩ高潮www| 成人午夜激情| 97A片在线观看播放| 国产调教视频| 五月综合色| 黄网在线看| 嫩草在线观看| 日韩一区二区三免费高清在线观看| 狠狠躁夜夜躁人人爽视频| 黑人一区二区三区四区| 成人午夜小视频| 最近中文字幕免费MV第一季歌词怀孕 | 欧美操b| 无码国产高清| 尿在小sao货里面好不好| 一区二区三级片| 东京热日韩无码| 在线乱视频| 四虎影院最新地址| 十八禁网站在线观看| 六月丁香久久| 伊人五月婷婷| 汇聚全球淫荡熟女| 黄频免费观看| 成人在线乱码视频| 亲子乱婬一级A片| 欧美爱爱网| 大香蕉亚洲| 国产黄色在线视频| 碰碰视频| 人人妻人人草| 久艹视频| 久操AV| 无码一二三区| 另类图片亚洲色图| A级成人网站| 神马久久午夜| eeuss一区| 日韩人妻无码中文字幕| 尤物网在线| 中文字幕人妻一区| 中文在线永久免费观看| 青青草超碰在线| 欧美囗交荫蒂AAAA| 久久99久久99久久99人受| 免费内射视频| 国精产品秘成人一区二| 日韩成人黄色电影| 国产操操操| 婷婷五月天成人电影| www.17c嫩嫩草色蜜桃网站 | 五月丁香婷婷综合| 日韩精品成人| 日韩一级黄色电影| 国产精品久久久久久久久久| 日本中文字幕在线观看| 黄片免费视频| 姐弟乱伦性爱| 91麻豆福利在线观看| www.天天射视频| 久久久亚洲AV无码精品色午夜| 美女视频黄a视频全免费不卡| 性做久久久久久| 青娱乐国产AV| 日本欧美一级片| 国产精品九九视频| 无码在线播| 黄色小视频在线| 免费av观看| 日韩永久免费| 麻豆精品无码| 特黄AAAAAAAA片免费直播| 国产区一区| 亚洲激情视频| 久久精品内射| 欧美丰满老熟妇XXXXX性| 欧美九九| 久久久久亚洲AV无码专区| 中文字幕无码亚| 狼人综合网| 成人日韩AV| 亚洲av男人天堂| 2012天天夜夜| 欧美亚洲图区| 亚洲砖区区免费| 日韩熟妇视频| 色婷婷婷| 91人妻人人| 插插网站| 免费的A片| 免费无码国产在线观看快色| 日韩黄色电影网站| 91成人在线观看国产| 2015中文字幕黄色视频| 中文字幕日韩一| 激情乱伦网站| 国产激情综合在线| 在线观看小视频| 最新毛片网站| 无码人妻一区二区三区免水牛视频 | 天堂网中文在线| 精品一本道| 在线a视频免费观看| 嫩BBB槡BBBB槡BBBB百度| 在线观看成人18| 国产免费一级片| 91热在线| 午夜成人黄片| 插逼综合网| 色aV牛牛在线观看| 亚洲精品一区中文字幕乱码| 欧美大鸡巴视频| 免费观看黄色AV| jizz久久| 日韩无码性爱视频| 久久免费视屏| 亚洲A片在线观看| 亚洲日韩中文字幕在线观看| 亚洲天堂无码a| 操一操干一干| 99天天视频| 国产熟睡乱子伦午夜视频_第1集| 国产久久在线观看| 大香蕉伊人AV| 91超碰在线免费观看| 五月天成人社区| 日本一级特黄电影| 国产一毛a一毛a在线观看| 唐嫣一级婬片A片AAA| 老太老熟女城中层露脸60| 一卡二卡三卡| 免费内射视频| 动漫3d啪啪成人h动漫| 伊人成人网站| 欧美日韩精品一区二区三区| 91人妻人人人| 黄色录像毛片| 国产剧情91| 黄色小视频在线免费观看| 国产高清不卡| 国产欧美综合视频一区二区在线 | 91熟女首页| 国产精品做爱| 美日韩在线| 亚洲秘一区二区三区-精品亚洲二区- | 女人的天堂AV| 免费一级无码婬片A片AAA毛片 | 中文熟妇| 日本九九视频| 激情国产在线| 亚洲无码免费看| 夜夜爽日日爽| 少妇高潮一区二区三区99| 韩国免费一级a一片在线播放 | 亚洲人一级电影| 国产一道本| 亚洲精品国偷拍自产在线观看蜜桃 | 成人一级电影| 高清中字无码| 午夜免费福利视频| 操B影院| 欧美精品一卡| 最新国产AV| 女人操逼视频| 日韩欧美性爱网站| 亚洲第一色婷婷| 97乱伦| 天堂中文字幕| 国产av一区二区三区| 爱爱免费视频| 日本老女人视频| 免费AV毛片| 国产女人18毛片水真多18精品| eeuss| 国产免费黄色视频网站| 精品视频网站| 激情丁香六月| 人人看人人摸人人| 日韩中字无码黄片| 国产亚洲视频免费观看| 安徽妇搡BBBB搡BBBB按摩| a片在线免费看| 先锋影音在线资源| 国产最新在线视频| 五月婷婷成人| 久久精品苍井空免费一区二| 天天操夜夜操狠狠操| 欧美视频操逼| 99视频自拍| 亚洲无码久久精品| 国产激情福利| www黄片| 按摩性高湖婬AAA片A片中国| 国产中文自拍| 黄色激情在线| 加勒比无码在线| 天天干天天上| 91熊猫| 看黄色一级片| 久久久久久网站| 操逼视频国产91| 国产成人中文字幕| 国产7777| 性v天堂| 日本在线www| 日韩AV中文字幕在线播放| 巜痴漢電車~凌脔版2| 91在线资源| 人人操人人干人人| 日本爱爱免费播放视频| 欧美淫乱视频| jizz国产| 国产一精品一aⅴ一免费| 一级爱爱爱| 91成人福利| 久久香蕉人| 就爱av| 无码人妻在线| 逼特逼在线观看| 欧美性爱无码在线| 俺去俺来也| 91美女网站| 一区二区三区精品婷婷| 特级西西人体www高清大胆| 日本高清不卡视频| 欧一美一婬一伦一区二区三区自慰 | www.天天干| 99在线小视频| 成人在线免费电影| 18禁网站在线| 亚洲视频网址| 亚洲天堂在线看| 操BAV| 一级A片视频免费看| 91香蕉视频免费| 亚洲欧美国产毛片在线| 久热中文字幕| 性爱福利导航| 一区二区三区免费在线观看| 国产无码激情视频| 国产久久视频在线观看| 熟妇熟女一区二区三区| 好吊看视频| 大香蕉免费中文| 日韩欧美国产综合| 三级片麻豆| 国产av探花| 亚洲在线成人| 真实国产乱子伦毛片| 就去色色五月丁香婷婷久久久| 另类BBwBBw| www.6969成人片亚洲| 东京热在线观看| 亚洲高清无码中字| 一区二区国产精品| 亚洲免费在线| 成人日韩AV| 伊人成人小说| 欧美激情色色| 日韩精品无码AV| 黑人一级| 亚州精品国产精品乱码不99勇敢| 亚洲第一成人网址| 国精品伦一区一区三区有限公司| 韩国中文字幕HD久久精品| 淫香淫色天天影视| 亚洲福利视频网| 人妻无码A| 欧美日韩亚洲一区二区| 婷婷五月天综合网| 成人伊人AV| 亚洲AV无码精品久久一区二区| 国产日韩精品无码去免费专区国产| 一级黄A片| 欧美成人在线免费视频| 狼友视频在线| av天天操| 无码区一区二区三区| 日韩高清国产一区在线| 日韩性爱一区| 狠狠干大香蕉| 久久久中文| 日本乱伦网| 日本免费一区二区三区| 六月婷婷在线|