<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    面試官:策略模式和代理模式有什么區(qū)別?

    共 19615字,需瀏覽 40分鐘

     ·

    2022-05-15 21:26

    大家好,我是田哥,昨天一哥們面試被問到代理模式,剛好,我也正在寫MyBatis源碼分析:小白系列》專欄中的代理模式。

    這里也說明一下,本文是MyBatis源碼分析專欄中的一篇文章。

    感興趣的可以掃描了解一下我的MyBatis源碼分析:小白系列》

    本文目錄:

    兩個有趣故事

    老田買火車票

    買火車票已經(jīng)是很老的話題了。老田以前每年都會坐綠皮車回家過年,幸運的是這兩年老家市區(qū)也有高鐵了,少了些許奔波。現(xiàn)在就來回憶下當年的情景:每到年底最頭疼的就是搶火車票,每次都需要去火車站排隊買票。因為只要網(wǎng)上的票一開始出售,基本上都是手慢無,最后就只能在周末時去火車站買票了。但是,有一次無意間聽說黃??梢詭兔I票,只是要付點手續(xù)費。于是,后面每當自己搶不到票時,都會走黃牛,費用也不是很高。相比自己苦逼到火車站買票,還是輕松很多的。

    ok,故事就講到這里,下面再使用java代碼來實現(xiàn)。

    未知黃牛黨之前

    //火車站
    public?interface?TrainStationService?{
    ????//坐公交去火車站
    ????void?byBus();
    ????//排隊買票
    ????void?lineUp();
    ????/**
    ?????*?買票
    ?????*?@param?idCard??身份證
    ?????*?@param?address?去往地址
    ?????*?@param?date????日期
    ?????*/

    ????void?buy(String?idCard,?String?address,?String?date);
    }
    //綠皮車站(老火車站)
    public?class?GreenSkinTrainStationServiceImpl?implements?TrainStationService?{

    ????@Override
    ????public?void?byBus()?{
    ????????System.out.println("坐公交去火車站");
    ????}

    ????@Override
    ????public?void?lineUp()?{
    ????????System.out.println("在火車站苦逼的排隊中....");
    ????}

    ????@Override
    ????public?void?buy(String?idCard,?String?address,?String?date)?{
    ????????System.out.println(idCard?+?"買到了一張通往"?+?address?+?"的綠皮車票,乘坐日期:"?+?date);
    ????}
    }
    //Client可以理解為老田
    public?class?Client?{
    ????public?static?void?main(String[]?args)?{
    ????????TrainStationService?trainStationService?=?new?GreenSkinTrainStationServiceImpl();
    ????????trainStationService.byBus();
    ????????trainStationService.lineUp();
    ????????trainStationService.buy("423268199901011234",?"老家",?"2019年2月1日");
    ????}
    }

    經(jīng)過一系列的折騰,老田的結果:

    坐公交去火車站 在火車站苦逼的排隊中.... 423268199901011234 買到了一張通往老家的綠皮車票,乘坐日期:2019年2月1日

    知道黃牛黨以后

    //黃牛黨
    public?class?CattlePerson?implements?TrainStationService{
    ????private?TrainStationService?trainStationService;

    ????public?CattlePerson()?{
    ????????this.trainStationService?=?=?new?GreenSkinTrainStationServiceImpl();
    ????}

    ????@Override
    ????public?void?byBus()?{
    ????????trainStationService.byBus();
    ????}
    ????@Override
    ????public?void?buy(String?idCard,?String?address,?String?date)?{
    ????????System.out.println("收手續(xù)費");
    ????????this.byBus();
    ????????this.lineUp();
    ????????trainStationService.buy(idCard,?address,?date);
    ????????System.out.println("黃牛黨把買到的票給老田");
    ????}
    ????@Override
    ????public?void?lineUp()?{
    ????????trainStationService.lineUp();
    ????}

    }
    //Client可以理解為老田
    public?class?Client?{
    ????public?static?void?main(String[]?args)?{
    ????????CattlePerson?cattlePerson=new?CattlePerson();
    ????????cattlePerson.buy("423268199901011234",?"老家",?"2019年2月1日");
    ????}
    }

    最后老田買車票結果是這樣的:

    黃牛黨收手續(xù)費 坐公交去火車站 在火車站苦逼的排隊中.... 423268199901011234 買到了一張通往老家的綠皮車票,乘坐日期:2019年2月1日 黃牛黨把買到的票給老田

    最終老田還是搞到票了,但是不用折騰自己了。

    老田玩王者農(nóng)藥

    盡管前兩年,乃至現(xiàn)在王者榮耀還是挺火的。也許有的人沒玩過,但是你絕對見過別人玩過。民間傳說“玩Dato的看不起玩英雄聯(lián)盟的,然而玩英雄聯(lián)盟的看不起玩王者榮耀”。哈哈哈,笑笑就可以了,管那么多,自己開心就好。這兩個游戲本人都玩過,但是沒有很投入。

    但是去年在同事老王的帶領下搞起了王者榮耀,瘋狂的時候可以玩通宵,大號不能玩了玩小號,小號不行就換 QQ 號......一直想上王者,悲哀的是一直沒上,記得最瘋狂的時候還是到了差一顆星就到星耀二,但是始終是上不了王者。無意間看到一個牛逼的玩家,說給他 388 塊就能上王者,才發(fā)現(xiàn)原來可以找人代打的,后面我細爬了一下,更恐怖的是有專門代打游戲的公司。由此可知,或許很多王者都是別人帶著或者代打上去的吧。

    下面用java來實現(xiàn)上面的兩種常見:

    老田玩王者農(nóng)藥

    public?interface?PlayerService?{
    ????//登錄
    ????void?login();
    ????//挑戰(zhàn)排位賽
    ????void?challenge();
    ????//升級
    ????void?upgrade();
    }
    public?class?PlayerServiceImpl?implements?PlayerService?{
    ????private?String?userName;
    ????private?String?password;

    ????public?PlayerServiceImpl(String?userName,?String?password)?{
    ????????this.userName?=?userName;
    ????????this.password?=?password;
    ????}

    ????@Override
    ????public?void?login()?{
    ????????//校驗用戶名和密碼
    ????????System.out.println("用戶:"?+?this.userName?+?",密碼:"?+?this.password);
    ????????System.out.println(this.userName?+?"登錄了");
    ????}

    ????@Override
    ????public?void?challenge()?{
    ????????System.out.println("挑戰(zhàn)排位賽比賽中...");
    ????}

    ????@Override
    ????public?void?upgrade()?{
    ????????System.out.println(this.userName?+?"又升級了");
    ????}
    }

    //Client當做老田
    public?class?Client{
    ????public?static?void?main(String[]?args)?{
    ????????PlayerService?playerService?=?new?PlayerServiceImpl("老田",?"123456");
    ????????playerService.login();
    ????????playerService.challenge();
    ????????playerService.upgrade();
    ????}
    }

    老田玩王者農(nóng)藥情況:

    用戶:老田,密碼:123456 老田登錄了 挑戰(zhàn)排位賽比賽中... 老田又升級了

    老田找人代打上王者

    也是要使用老田的賬號登錄,也是要一場一場的排位,最后達上王者。不知道能代打游戲之前老田是自己傻傻的一場一場打的。

    //替人打游戲的人或公司
    public?class?PlayerProxy?implements?PlayerService?{
    ????private?PlayerService?playerService;
    ????/**
    ?????*?費用
    ?????*/

    ????private?BigDecimal?fee;
    ????private?String?userName;
    ????private?String?password;

    ????public?PlayerProxy(String?userName,?String?password,?BigDecimal?fee)?{
    ????????this.playerService?=?new?PlayerServiceImpl(userName,?password);
    ????????this.fee?=?fee;
    ????????this.userName?=?userName;
    ????????this.password?=?password;
    ????}

    ????@Override
    ????public?void?login()?{
    ????????playerService.login();
    ????}

    ????@Override
    ????public?void?player()?{
    ????????System.out.println("代理商賺取手續(xù)費"?+?fee);
    ????????this.login();
    ????????playerService.player();
    ????????this.upgrade();
    ????}

    ????@Override
    ????public?void?upgrade()?{
    ????????playerService.upgrade();
    ????}
    }
    //老王
    public?class?Client?{
    ????public?static?void?main(String[]?args)?{
    ????????//告訴代理商或者代打的人賬戶名+密碼+費用
    ????????PlayerProxy?playerProxy?=?new?PlayerProxy("老田",?"123456",?BigDecimal.valueOf(100));
    ????????playerProxy.player();
    ????}
    }

    最后老田只要等著別人給他打好上王者。

    代理商收手續(xù)費100 用戶:老田,密碼:123456 老田登錄了 挑戰(zhàn)排位賽比賽中... 老田滿五星升級

    上面兩個故事中分別有兩個很重要的角色:黃牛黨和代打游戲的人。

    有黃牛黨后老田不用關心票是怎么買的、有了代打游戲的人以后老田也不用關系是怎么上王者的,都全權交給他們去干。

    同樣的生活中的例子:相親找媒婆,租房子找中介等,都是中間有個代你辦事干活的人。

    以上舉的例子就是傳說中的代理模式。

    代理模式的定義

    Provide?a?surrogate?or?placeholder?for?another?object?to?control?access?to?it.

    代理模式就是由于某些原因需要給某對象提供一個代理以控制對該對象的訪問。這時,訪問對象不適合或者不能直接引用目標對象,代理對象作為訪問對象和目標對象之間的中介。

    代理模式是GOF的23種設計模式之一(結構型模式)。

    代理模式也叫委托模式,它是一種基本設計技巧,許多其他的設計模式,比如:狀態(tài)模式,策略模式,訪問者模式本質上是在更特殊的場合采用了委托模式。

    角色

    Subject:抽象主題,抽象主題類可以是抽象類也可以是接口,是一個最普通的業(yè)務類型定義,沒有特殊要求。

    RealSubject:真實主題,也叫被委托角色,被代理角色。他是業(yè)務邏輯的具體執(zhí)行者。

    Proxy:代理,也叫委托類,代理類。它負責對真實角色的應用,把所有抽象主題定義的方法限制委托給真實主題去實現(xiàn),并且在真實主題角色處理完畢的前后做預處理和善后處理工作。

    通用UML

    通用java代碼

    以上三個角色的java代碼實現(xiàn)

    public?interface?Subject?{
    ????void?request();
    }
    public?class?RealSbject?implements?Subject?{
    ????@Override
    ????public?void?request()?{
    ??????System.out.println("訪問具體主題角色方法...");
    ????}
    }
    public?class?Proxy?implements?Subject?{
    ????private?RealSubject?realSubject;

    ????public?Proxy()?{
    ????????this.realSubject?=?new?RealSubject();
    ????}

    ????@Override
    ????public?void?request()?{
    ????????preRequest();
    ????????realSubject.request();
    ????????postRequest();
    ????}

    ????public?void?preRequest()?{
    ????????System.out.println("訪問真實主題之前的預處理。");
    ????}

    ????public?void?postRequest()?{
    ????????System.out.println("訪問真實主題之后的后續(xù)處理。");
    ????}
    }
    public?class?ProxyClient?{
    ????public?static?void?main(String[]?args)?{
    ????????Proxy?proxy=new?Proxy();
    ????????proxy.request();
    ????}
    }

    運行結果:

    訪問真實主題之前的預處理。訪問具體主題角色方法... 訪問真實主題之后的后續(xù)處理。

    優(yōu)缺點

    優(yōu)點:效率高,只要獲取代理對象并執(zhí)行就結束了

    缺點:如果接口增加新方法,被代理類得改,代理也得改;每一個被代理類都得有一個代理類。如果系統(tǒng)很大,那么關于代理類的維護的代價是很大的。

    裝飾器模式

    裝飾器模式是結構性模式之一,裝飾模式指的是在不必改變原類文件和使用繼承的情況下,動態(tài)地擴展一個對象的功能。它是通過創(chuàng)建一個包裝對象,也就是裝飾來包裹真實的對象。也可以稱之為包裝模式

    特點:動態(tài)添加或者覆蓋被裝飾的接口/抽象類行為。

    關系:裝飾者和被裝飾者有著接口/抽象類層次關系。

    角色

    抽象組件(Component)角色:定義一個將要接收附加責任的類,即繼承該抽象類的類都有了裝飾和被裝飾的能力。

    具體組件(ConcreteComponent)角色:可以被動態(tài)加上新行為,被裝飾者修飾的類。

    裝飾者(Decorator)角色:裝飾者抽象類,繼承該類都具有裝飾者的能力。

    具體裝飾者(ConcreteDecorator)角色:為具體組件添加新行為。

    通用UML圖

    通用java代碼

    //抽象構件
    public?abstract?class?Component?{
    ????public?abstract?void?operate();
    }
    //具體構件(最終被裝飾的類)
    public?class?ConcreteComponent?extends?Component?{
    ????@Override
    ????public?void?operate()?{
    ????????System.out.println("doSomething");
    ????}
    }
    //抽象裝飾者
    public?class?Decorator?extends?Component?{
    ????private?Component?component;
    ????//通過構造函數(shù)傳遞被裝飾者
    ????public?Decorator(Component?component)?{
    ????????this.component?=?component;
    ????}
    ????//委托給被裝飾者執(zhí)行
    ????@Override
    ????public?void?operate()?{
    ????????this.component.operate();
    ????}
    }
    //具體裝飾類
    public?class?ConcreteDecorator?extends?Decorator?{
    ????//定義被裝飾者
    ????public?ConcreteDecorator(Component?component)?{
    ????????super(component);
    ????}

    ????private?void?before()?{
    ????????System.out.println("在調用operate方法前給你添加點東西");
    ????}

    ????@Override
    ????public?void?operate()?{
    ????????//調用前就行裝飾
    ????????this.before();
    ????????//調用具體被裝飾類的方法
    ????????super.operate();
    ????????//調用后進行裝飾
    ????????this.after();
    ????}

    ????private?void?after()?{
    ????????System.out.println("在調用operate方法后給你添加點東西");
    ????}
    }
    //測試
    public?class?Client?{
    ????public?static?void?main(String[]?args)?{
    ????????Component?component?=?new?ConcreteComponent();
    ????????Decorator?decorator?=?new?ConcreteDecorator(component);
    ????????decorator.operate();
    ????}
    }

    運行結果:

    在調用operate方法前給你添加點東西 doSomething 在調用operate方法后給你添加點東西

    demo 案例

    簡單支付場景,請看代碼實現(xiàn)

    public?interface?PayService?{
    ????//支付
    ????void?pay();
    }
    //被裝飾的類--支付場景
    public?class?PayServiceImpl?implements?PayService?{
    ????@Override
    ????public?void?pay()?{
    ????????System.out.println("執(zhí)行PayServiceImpl--的--支付--支付方法");
    ????}
    }
    public?interface?PayParamsMsgService?extends?PayService?{
    ????//支付
    ????@Override
    ????void?pay();
    ????//發(fā)站內信
    ????void?sendMsg();
    ????//參數(shù)校驗
    ????void?checkParams();
    }
    //裝飾類
    public?class?PayParamsMsgServiceImpl?implements?PayParamsMsgService?{
    ????private?PayService?payService;

    ????public?PayParamsMsgServiceImpl(PayService?payService)?{
    ????????this.payService?=?payService;
    ????}
    ????//沒有被裝飾的支付=支付
    ????//裝飾后的支付=支付前進行參數(shù)校驗-->支付-->支付后發(fā)生信息
    ????@Override
    ????public?void?pay()?{
    ????????checkParams();
    ????????payService.pay();
    ????????sendMsg();
    ????}
    ????@Override
    ????public?void?sendMsg()?{
    ????????System.out.println("發(fā)送支付成功站內信");
    ????}
    ????@Override
    ????public?void?checkParams()?{
    ????????System.out.println("校驗余額是否足夠,校驗密碼是否非法登錄等");
    ????}
    }
    public?class?DemoClient?{
    ????public?static?void?main(String[]?args)?{
    ????????PayService?payService=new?PayServiceImpl();
    ????????PayService?payService1=new?PayParamsMsgServiceImpl(payService);
    ????????payService1.pay();
    ????}
    }

    運行結果:

    校驗余額是否足夠,校驗密碼是否非法登錄等 執(zhí)行PayServiceImpl--的--支付--支付方法 發(fā)送支付成功站內信

    優(yōu)缺點

    優(yōu)點

    • 裝飾類和被裝飾類可以獨立發(fā)展,而不會相互耦合。說明白了就是Component類無須知道Decorator類,Decorator類是從外部來擴展Component,而Decorator也不知道具體的構件。
    • 裝飾模式是繼承關系的一種替代方案。咱們看裝飾類,不管裝飾多少層,返回的對象還是Component,實現(xiàn)的還是is-a的關系。
    • 裝飾模式可以動態(tài)擴展一個實現(xiàn)類的功能,這不需多說,裝飾模式定義就是這么講的。

    缺點:多層裝飾比較復雜。

    使用場景

    1、擴展一個類的功能

    2、動態(tài)增加功能,動態(tài)撤銷。

    開發(fā)過程中,一般是針對老系統(tǒng)或者已經(jīng)正常使用的功能之外添加一些新的功能。不建議新系統(tǒng)使用裝飾器設計模式。

    代理模式 VS 裝飾器模式

    這兩個模式在實現(xiàn)上很容易陷入難分難解的地步,但是請記住他們的本質區(qū)別:裝飾模式重在裝飾、增強,而代理模式重在訪問的權限控制。

    如果你寫了一個方法,后續(xù)考慮要給這個方法添加一些功能,可以選擇裝飾模式。如果你寫了一堆方法,后續(xù)考慮統(tǒng)一給這些方法添加功能(比如日志記錄,資源回收),可以選擇代理模式。

    靜態(tài)代理

    字面意義就是靜態(tài)的代理模式,在上面的幾個代理模式案例中,所有的代理類都是咱們自己手寫的,所以是已經(jīng)定好的,也就可以理解為是死的、靜態(tài)的,由此可知上面案例都是靜態(tài)代理模式。

    定義:由程序員創(chuàng)建或工具生成代理類的源碼,再編譯代理類。所謂靜態(tài)也就是在程序運行前就已經(jīng)存在代理類的字節(jié)碼文件,代理類和委托類的關系在運行前就確定了。

    代理模式就是靜態(tài)代理模式。就是被代理對象和代理類都要實現(xiàn)同一個接口。

    既然有靜態(tài)代理,不妨假設一下,這個代理不需要咱們來寫,那豈不就是動態(tài)代理嗎?

    動態(tài)代理

    何為動態(tài)代理?

    動態(tài)代理是在實現(xiàn)階段不用關心代理誰,而是在運行階段才指定代理哪一個對象。上面得案例中的代理類必須是自己創(chuàng)建的,人都知道偷懶,所以完全不想寫那么多代碼了(代理類),所以得動態(tài)生成。JDK動態(tài)代理能解決這個問題;

    JDK動態(tài)代理

    JDK動態(tài)代理是一種代理模式實現(xiàn)之一,只能代理接口。

    通用java代碼實現(xiàn)
    //第一步
    public?interface?Subject?{
    ????void?request();
    }
    //第二步
    public?class?RealSubject?implements?Subject?{
    ????@Override
    ????public?void?request()?{
    ????????System.out.println("訪問具體主題角色方法...");
    ????}
    }
    //第三步
    public?class?JDKDynamicProxy?implements?InvocationHandler?{
    ????/**
    ?????*?將目標對象傳入進行代理
    ?????*/

    ????private?Object?target;

    ????public?JDKDynamicProxy(Object?target)?{
    ????????this.target?=?target;
    ????}

    ????/**
    ?????*?獲取被代理接口實例對象
    ?????*/

    ????public??T?getProxy()?{
    ????????return?(T)?Proxy.newProxyInstance(target.getClass().getClassLoader(),?target.getClass().getInterfaces(),?this);
    ????}
    ????/**
    ????*實現(xiàn)增強邏輯和對真實對象方法的調用
    ????*@param?proxy?生成的動態(tài)代理對象
    ????*@param?method?動態(tài)代理在客戶端執(zhí)行的方法
    ????*@param?args?該方法的參數(shù)列表
    ????*/

    ????@Override
    ????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
    ????????before();
    ????????Object?result?=?method.invoke(target,?args);
    ????????after();
    ????????return?result;
    ????}

    ????private?void?before()?{//方法執(zhí)行前
    ????????System.out.println("方法執(zhí)行前");
    ????}

    ????private?void?after()?{//方法執(zhí)行后
    ????????System.out.println("方法執(zhí)行后");
    ????}
    }
    //第四步
    public?class?Client?{
    ????public?static?void?main(String[]?args)?{
    ????????//保存生成的代理類的字節(jié)碼文class文件:$Proxy0.class
    ????????//下面會細說$Proxy0.class
    ????????System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles",?"true");
    ????????Subject?target?=?new?RealSubject();
    ????????JDKDynamicProxy?jdkDynamicProxy?=?new?JDKDynamicProxy(target);
    ????????Subject?proxy?=?jdkDynamicProxy.getProxy();
    ????????proxy.request();
    ????}
    }

    運行結果:

    方法執(zhí)行前 訪問具體主題角色方法... 方法執(zhí)行后

    現(xiàn)在有了JDKDynamicProxy,那么我們就再也不需要手動寫代理類了,而是交給JDK去幫我們自動創(chuàng)建。細心的人你應該會發(fā)現(xiàn)JDK的動態(tài)代理通用java代碼和裝飾模式幾乎一毛一樣,但是請記住他們的目的不同,裝飾器設計模式是給一個對象動態(tài)的增加方法(功能),而動態(tài)代理的目的是控制被代理對象的訪問權。

    JDK動態(tài)代理四大步驟
    1. 定義業(yè)務接口
    2. 被代理對象實現(xiàn)業(yè)務接口
    3. 通過Proxy的靜態(tài)方法newProxyInstance( ClassLoader loader, Class[] interfaces, InvocationHandler h)創(chuàng)建一個代理對象
    4. 使用代理對象
    JDK動態(tài)代理源碼分析

    JDK動態(tài)代理四個步驟中最讓人迷惑的就是第三步,第三步到底做了什么?(源碼分析挺乏味的,但是請記住你不讀源碼很多東西永遠只停留在了解或者皮毛階段,閱讀源碼至少會讓你知道怎么寫出高質量的代碼)

    進入java.lang.reflect.Proxy類中的

    ?public?static?Object?newProxyInstance(ClassLoader?loader,??Class[]?interfaces,????????????????????????????????????????????InvocationHandler?h)?throws?IllegalArgumentException?{
    ????????//?判斷參數(shù)h是否為空,咱們這里的h=new?JDKDynamicProxy()
    ????????Objects.requireNonNull(h);
    ????????//接口數(shù)組先拷貝一份
    ????????final?Class[]?intfs?=?interfaces.clone();
    ????????//安全檢查
    ????????final?SecurityManager?sm?=?System.getSecurityManager();
    ????????if?(sm?!=?null)?{
    ????????????//檢查創(chuàng)建代理類所需的權限
    ????????????checkProxyAccess(Reflection.getCallerClass(),?loader,?intfs);
    ????????}
    ????????//最關鍵的地方
    ????????//查詢(在緩存中已經(jīng)有)或生成指定的代理類的class對象。后面細說
    ????????Class?cl?=?getProxyClass0(loader,?intfs);
    ????????try?{
    ????????????if?(sm?!=?null)?{
    ????????????????checkNewProxyPermission(Reflection.getCallerClass(),?cl);
    ????????????}
    ????????????//用invocationHandler生成構造函數(shù)
    ????????????final?Constructor?cons?=?cl.getConstructor(constructorParams);
    ????????????final?InvocationHandler?ih?=?h;
    ????????????if?(!Modifier.isPublic(cl.getModifiers()))?{
    ????????????????AccessController.doPrivileged(new?PrivilegedAction()?{
    ????????????????????public?Void?run()?{
    ????????????????????????cons.setAccessible(true);
    ????????????????????????return?null;
    ????????????????????}
    ????????????????});
    ????????????}
    ????????????return?cons.newInstance(new?Object[]{h});
    ????????}?catch?(IllegalAccessException|InstantiationException?e)?{
    ????????????throw?new?InternalError(e.toString(),?e);
    ????????}?catch?(InvocationTargetException?e)?{
    ????????????Throwable?t?=?e.getCause();
    ????????????if?(t?instanceof?RuntimeException)?{
    ????????????????throw?(RuntimeException)?t;
    ????????????}?else?{
    ????????????????throw?new?InternalError(t.toString(),?t);
    ????????????}
    ????????}?catch?(NoSuchMethodException?e)?{
    ????????????throw?new?InternalError(e.toString(),?e);
    ????????}
    }
    private?static?Class?getProxyClass0(ClassLoader?loader,?Class...?interfaces)?{
    ????????////?限定代理的接口不能超過65535個
    ????????if?(interfaces.length?>?65535)?{
    ????????????throw?new?IllegalArgumentException("interface?limit?exceeded");
    ????????}
    ????????////?如果緩存中已經(jīng)存在相應接口的代理類,直接返回;否則,使用ProxyClassFactory創(chuàng)建代理類
    ????????return?proxyClassCache.get(loader,?interfaces);
    ????}

    //Proxy的靜態(tài)內部類ProxyClassFactory????
    private?static?final?class?ProxyClassFactory
    ????????implements?BiFunction<ClassLoader,?Class[],?Class>
    ????
    {
    ????????//?prefix?for?all?proxy?class?names?代理類前綴
    ????????private?static?final?String?proxyClassNamePrefix?=?"$Proxy";
    ????????//?next?number?to?use?for?generation?of?unique?proxy?class?names
    ????????//生成代理類名稱的計數(shù)器
    ????????private?static?final?AtomicLong?nextUniqueNumber?=?new?AtomicLong();

    ????????@Override
    ????????public?Class?apply(ClassLoader?loader,?Class[]?interfaces)?{

    ????????????Map,?Boolean>?interfaceSet?=?new?IdentityHashMap<>(interfaces.length);
    ????????????for?(Class?intf?:?interfaces)?{
    ????????????????//校驗類加載器是否能通過接口名稱加載該類
    ????????????????Class?interfaceClass?=?null;
    ????????????????try?{
    ????????????????????//根據(jù)接口全限定類名和classLoader,獲取到接口class對象
    ????????????????????interfaceClass?=?Class.forName(intf.getName(),?false,?loader);
    ????????????????}?catch?(ClassNotFoundException?e)?{
    ????????????????}
    ????????????????//如果兩次接口class對象不一致,直接拋出異常,說明創(chuàng)建錯誤
    ????????????????if?(interfaceClass?!=?intf)?{
    ????????????????????throw?new?IllegalArgumentException(
    ????????????????????????intf?+?"?is?not?visible?from?class?loader");
    ????????????????}
    ????????????????//校驗該類是否是接口類型,這里就是證明為什么JDK動態(tài)代理是代理接口的。
    ????????????????if?(!interfaceClass.isInterface())?{
    ????????????????????throw?new?IllegalArgumentException(
    ????????????????????????interfaceClass.getName()?+?"?is?not?an?interface");
    ????????????????}
    ????????????????//
    ????????????????if?(interfaceSet.put(interfaceClass,?Boolean.TRUE)?!=?null)?{
    ????????????????????throw?new?IllegalArgumentException(
    ????????????????????????"repeated?interface:?"?+?interfaceClass.getName());
    ????????????????}
    ????????????}
    ???????????//代理類包名
    ????????????String?proxyPkg?=?null;?????
    ????????????int?accessFlags?=?Modifier.PUBLIC?|?Modifier.FINAL;
    ????????????//非public接口,代理類的包名與接口的包名相同
    ????????????for?(Class?intf?:?interfaces)?{
    ????????????????int?flags?=?intf.getModifiers();
    ????????????????if?(!Modifier.isPublic(flags))?{
    ????????????????????accessFlags?=?Modifier.FINAL;
    ????????????????????String?name?=?intf.getName();
    ????????????????????int?n?=?name.lastIndexOf('.');
    ????????????????????String?pkg?=?((n?==?-1)???""?:?name.substring(0,?n?+?1));
    ????????????????????if?(proxyPkg?==?null)?{
    ????????????????????????proxyPkg?=?pkg;
    ????????????????????}?else?if?(!pkg.equals(proxyPkg))?{
    ????????????????????????throw?new?IllegalArgumentException(
    ????????????????????????????"non-public?interfaces?from?different?packages");
    ????????????????????}
    ????????????????}
    ????????????}
    ????????????//如果都是public接口設定全限定類名com.sun.proxy.
    ????????????if?(proxyPkg?==?null)?{
    ????????????????//public代理接口,使用ReflectUtil.PROXY_PACKAGE=com.sun.proxy.包名
    ????????????????proxyPkg?=?ReflectUtil.PROXY_PACKAGE?+?".";
    ????????????}
    ????????????//為代理類生成名字
    ????????????long?num?=?nextUniqueNumber.getAndIncrement();
    ????????????String?proxyName?=?proxyPkg?+?proxyClassNamePrefix?+?num;
    ????????????//真正生成代理類的字節(jié)碼文件的地方:ProxyGenerator類中
    ????????????byte[]?proxyClassFile?=?ProxyGenerator.generateProxyClass(proxyName,?interfaces,?accessFlags);
    ????????????try?{
    ????????????????//使用類加載器將代理類的字節(jié)碼文件加載到JVM中
    ????????????????return?defineClass0(loader,?proxyName,?proxyClassFile,?0,?proxyClassFile.length);
    ????????????}?catch?(ClassFormatError?e)?{
    ????????????????throw?new?IllegalArgumentException(e.toString());
    ????????????}
    ????????}
    ????}????
    ????//ProxyGenerator類中
    ????public?static?byte[]?generateProxyClass(final?String?var0,?Class[]?var1,?int?var2)?{
    ????????ProxyGenerator?var3?=?new?ProxyGenerator(var0,?var1,?var2);
    ????????final?byte[]?var4?=?var3.generateClassFile();
    ????????//是否要將生成代理類的字節(jié)碼文件保存到磁盤中
    ????????if?(saveGeneratedFiles)?{
    ????????????AccessController.doPrivileged(new?PrivilegedAction()?{
    ????????????????public?Void?run()?{
    ????????????????????try?{
    ????????????????????????int?var1?=?var0.lastIndexOf(46);
    ????????????????????????Path?var2;
    ????????????????????????if?(var1?>?0)?{
    ????????????????????????????Path?var3?=?Paths.get(var0.substring(0,?var1).replace('.',?File.separatorChar));
    ????????????????????????????Files.createDirectories(var3);
    ????????????????????????????var2?=?var3.resolve(var0.substring(var1?+?1,?var0.length())?+?".class");
    ????????????????????????}?else?{
    ????????????????????????????var2?=?Paths.get(var0?+?".class");
    ????????????????????????}
    ????????????????????????//如果找不到生成$Proxy0.class可以write方法一直追中下去,將會看到具體的保存路徑
    ????????????????????????//WindowsFileSystemProvider
    ????????????????????????Files.write(var2,?var4,?new?OpenOption[0]);
    ????????????????????????return?null;
    ????????????????????}?catch?(IOException?var4x)?{
    ????????????????????????throw?new?InternalError("I/O?exception?saving?generated?file:?"?+?var4x);
    ????????????????????}
    ????????????????}
    ????????????});
    ????????}
    ????????return?var4;
    ?}
    //具體生成class字節(jié)碼文件的方法
    ?private?byte[]?generateClassFile()?{
    ????????//將object類當中的?hashcode,equals,toString方法添加到動態(tài)代理類當中
    ????????//addProxyMethod方法后面有分析
    ????????this.addProxyMethod(hashCodeMethod,?Object.class);
    ????????this.addProxyMethod(equalsMethod,?Object.class);
    ????????this.addProxyMethod(toStringMethod,?Object.class);
    ????????Class[]?var1?=?this.interfaces;
    ????????int?var2?=?var1.length;
    ????????int?var3;
    ????????Class?var4;
    ????????//遍歷父接口數(shù)據(jù)
    ????????for(var3?=?0;?var3?????????????var4?=?var1[var3];
    ????????????//獲取每個接口當中的方法
    ????????????Method[]?var5?=?var4.getMethods();
    ????????????int?var6?=?var5.length;
    ?????????????//遍歷接口當中的方法,將接口當中的方法都添加至動態(tài)代理類當中
    ????????????for(int?var7?=?0;?var7?????????????????Method?var8?=?var5[var7];
    ????????????????this.addProxyMethod(var8,?var4);
    ????????????}
    ????????}
    ????????Iterator?var11?=?this.proxyMethods.values().iterator();
    ???????//檢查代理類當中的返回類型
    ????????List?var12;
    ????????while(var11.hasNext())?{
    ????????????var12?=?(List)var11.next();
    ????????????checkReturnTypes(var12);
    ????????}
    ????????Iterator?var15;
    ????????try?{
    ?????????????//?將構造方法添加至代理類當中的方法集合中
    ????????????this.methods.add(this.generateConstructor());
    ????????????var11?=?this.proxyMethods.values().iterator();
    ????????????//遍歷代理類當中的方法,此處使用兩層循環(huán),是因為方法簽名相同的,可能有多個方法
    ????????????while(var11.hasNext())?{
    ????????????????var12?=?(List)var11.next();
    ????????????????var15?=?var12.iterator();
    ????????????????while(var15.hasNext())?{
    ????????????????????ProxyGenerator.ProxyMethod?var16?=?(ProxyGenerator.ProxyMethod)var15.next();
    ????????????????????this.fields.add(new?ProxyGenerator.FieldInfo(var16.methodFieldName,?"Ljava/lang/reflect/Method;",?10));
    ????????????????????this.methods.add(var16.generateMethod());
    ????????????????}
    ????????????}
    ????????????//?將靜態(tài)代碼塊添加進去
    ????????????this.methods.add(this.generateStaticInitializer());
    ????????}?catch?(IOException?var10)?{
    ????????????throw?new?InternalError("unexpected?I/O?Exception",?var10);
    ????????}
    ????????//方法個數(shù)不能超過65535
    ????????if?(this.methods.size()?>?65535)?{
    ????????????throw?new?IllegalArgumentException("method?limit?exceeded");
    ????????}?else?if?(this.fields.size()?>?65535)?{//屬性不能超過65535
    ????????????throw?new?IllegalArgumentException("field?limit?exceeded");
    ????????}?else?{
    ????????????//編寫最終類文件
    ????????????//在開始編寫最終類文件之前,確保為下面的項目保留常量池索引
    ????????????this.cp.getClass(dotToSlash(this.className));
    ????????????this.cp.getClass("java/lang/reflect/Proxy");
    ????????????var1?=?this.interfaces;
    ????????????var2?=?var1.length;

    ????????????for(var3?=?0;?var3?????????????????var4?=?var1[var3];
    ????????????????this.cp.getClass(dotToSlash(var4.getName()));
    ????????????}
    ????????????//設置只讀,在這之前不允許在常量池中增加信息,因為要寫常量池表
    ????????????this.cp.setReadOnly();

    ????????????ByteArrayOutputStream?var13?=?new?ByteArrayOutputStream();
    ????????????DataOutputStream?var14?=?new?DataOutputStream(var13);

    ????????????try?{
    ????????????????//magic魔法數(shù)字
    ????????????????var14.writeInt(-889275714);
    ????????????????//次版本
    ????????????????var14.writeShort(0);
    ????????????????//主版本
    ????????????????var14.writeShort(49);
    ????????????????this.cp.write(var14);
    ????????????????//訪問表示
    ????????????????var14.writeShort(this.accessFlags);
    ????????????????//本類名稱
    ????????????????var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
    ????????????????var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
    ????????????????//接口
    ????????????????var14.writeShort(this.interfaces.length);
    ????????????????Class[]?var17?=?this.interfaces;
    ????????????????int?var18?=?var17.length;

    ????????????????for(int?var19?=?0;?var19?????????????????????Class?var22?=?var17[var19];
    ????????????????????var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
    ????????????????}
    ????????????????//字段
    ????????????????var14.writeShort(this.fields.size());
    ????????????????var15?=?this.fields.iterator();

    ????????????????while(var15.hasNext())?{
    ????????????????????ProxyGenerator.FieldInfo?var20?=?(ProxyGenerator.FieldInfo)var15.next();
    ????????????????????var20.write(var14);
    ????????????????}
    ????????????????//方法
    ????????????????var14.writeShort(this.methods.size());
    ????????????????var15?=?this.methods.iterator();

    ????????????????while(var15.hasNext())?{
    ????????????????????ProxyGenerator.MethodInfo?var21?=?(ProxyGenerator.MethodInfo)var15.next();
    ????????????????????var21.write(var14);
    ????????????????}
    ????????????????//類文件屬性:對于代理類來說沒有類文件屬性
    ????????????????////?(no?ClassFile?attributes?for?proxy?classes)
    ????????????????var14.writeShort(0);
    ????????????????return?var13.toByteArray();
    ????????????}?catch?(IOException?var9)?{
    ????????????????throw?new?InternalError("unexpected?I/O?Exception",?var9);
    ????????????}
    ????????}
    ????}
    ???//給代理類添加方法
    ????private?void?addProxyMethod(Method?var1,?Class?var2)?{
    ????????//方法名
    ????????String?var3?=?var1.getName();
    ????????//方法參數(shù)類型
    ????????Class[]?var4?=?var1.getParameterTypes();
    ????????//返回值類型
    ????????Class?var5?=?var1.getReturnType();
    ????????//異常類型
    ????????Class[]?var6?=?var1.getExceptionTypes();
    ????????//方法簽名
    ????????String?var7?=?var3?+?getParameterDescriptors(var4);
    ????????//根據(jù)方法簽名卻獲得proxyMethods的Value
    ????????Object?var8?=?(List)this.proxyMethods.get(var7);
    ????????//處理多個代理接口中重復的方法的情況
    ????????if?(var8?!=?null)?{
    ????????????Iterator?var9?=?((List)var8).iterator();

    ????????????while(var9.hasNext())?{
    ????????????????ProxyGenerator.ProxyMethod?var10?=?(ProxyGenerator.ProxyMethod)var9.next();
    ????????????????if?(var5?==?var10.returnType)?{
    ????????????????????//歸約異常類型以至于讓重寫的方法拋出合適的異常類型,我認為這里可能是多個接口中有相同的方法,
    ????????????????????//而這些相同的方法拋出的異常類型又不同,
    ????????????????????//所以對這些相同方法拋出的異常進行了歸約
    ????????????????????ArrayList?var11?=?new?ArrayList();
    ????????????????????collectCompatibleTypes(var6,?var10.exceptionTypes,?var11);
    ????????????????????collectCompatibleTypes(var10.exceptionTypes,?var6,?var11);
    ????????????????????var10.exceptionTypes?=?new?Class[var11.size()];
    ????????????????????//將ArrayList轉換為Class對象數(shù)組
    ????????????????????var10.exceptionTypes?=?(Class[])var11.toArray(var10.exceptionTypes);
    ????????????????????return;
    ????????????????}
    ????????????}
    ????????}?else?{
    ????????????var8?=?new?ArrayList(3);
    ????????????this.proxyMethods.put(var7,?var8);
    ????????}
    ????????((List)var8).add(new?ProxyGenerator.ProxyMethod(var3,?var4,?var5,?var6,?var2,?null));
    ????}

    自此源碼分析完畢?,F(xiàn)在回過頭去看自動生成的代理類里到底有什么東東?

    package?com.sun.proxy;

    import?com.tian.swagger.proxy.Subject;
    import?java.lang.reflect.InvocationHandler;
    import?java.lang.reflect.Method;
    import?java.lang.reflect.Proxy;
    import?java.lang.reflect.UndeclaredThrowableException;
    //繼承了Proxy,實現(xiàn)了Subject
    public?final?class?$Proxy0?extends?Proxy?implements?Subject?{
    ????private?static?Method?m1;
    ????private?static?Method?m2;
    ????private?static?Method?m3;
    ????private?static?Method?m0;
    ????//代理類的構造函數(shù),其參數(shù)正是是InvocationHandler實例,
    ????//Proxy.newInstance方法就是通過通過這個構造函數(shù)來創(chuàng)建代理實例的
    ????public?$Proxy0(InvocationHandler?var1)?throws??{
    ????????super(var1);
    ????}

    ????public?final?boolean?equals(Object?var1)?throws??{
    ????????try?{
    ????????????return?((Boolean)super.h.invoke(this,?m1,?new?Object[]{var1})).booleanValue();
    ????????}?catch?(RuntimeException?|?Error?var3)?{
    ????????????throw?var3;
    ????????}?catch?(Throwable?var4)?{
    ????????????throw?new?UndeclaredThrowableException(var4);
    ????????}
    ????}

    ????public?final?String?toString()?throws??{
    ????????try?{
    ????????????return?(String)super.h.invoke(this,?m2,?(Object[])null);
    ????????}?catch?(RuntimeException?|?Error?var2)?{
    ????????????throw?var2;
    ????????}?catch?(Throwable?var3)?{
    ????????????throw?new?UndeclaredThrowableException(var3);
    ????????}
    ????}

    ????public?final?int?hashCode()?throws??{
    ????????try?{
    ????????????return?((Integer)super.h.invoke(this,?m0,?(Object[])null)).intValue();
    ????????}?catch?(RuntimeException?|?Error?var2)?{
    ????????????throw?var2;
    ????????}?catch?(Throwable?var3)?{
    ????????????throw?new?UndeclaredThrowableException(var3);
    ????????}
    ????}
    ????//上面三個方法對應的是hashCode、toString、equals

    ????//重寫接口Subject中定義的方法request
    ????public?final?void?request()?throws??{
    ????????try?{
    ????????????//h就是Proxy類中的變量protected?InvocationHandler?h;
    ????????????//this就是當前$Proxy0對象;
    ????????????//m3就是Class.forName("com.tian.swagger.proxy.Subject").getMethod("request",?new?Class[0]);
    ????????????//即是通過全路徑名,反射獲取的目標對象中的真實方法加參數(shù)
    ????????????super.h.invoke(this,?m3,?(Object[])null);
    ????????}?catch?(RuntimeException?|?Error?var2)?{
    ????????????throw?var2;
    ????????}?catch?(Throwable?var3)?{
    ????????????throw?new?UndeclaredThrowableException(var3);
    ????????}
    ????}
    ????//?靜態(tài)代碼塊對變量進行一些初始化工作
    ????static?{
    ????????try?{
    ????????????//這里每個方法對象?和類的實際方法綁定
    ????????????m1?=?Class.forName("java.lang.Object").getMethod("equals",?Class.forName("java.lang.Object"));
    ????????????m2?=?Class.forName("java.lang.Object").getMethod("toString");
    ????????????m3?=?Class.forName("com.tian.swagger.proxy.Subject").getMethod("request");
    ????????????m0?=?Class.forName("java.lang.Object").getMethod("hashCode");
    ????????}?catch?(NoSuchMethodException?var2)?{
    ????????????throw?new?NoSuchMethodError(var2.getMessage());
    ????????}?catch?(ClassNotFoundException?var3)?{
    ????????????throw?new?NoClassDefFoundError(var3.getMessage());
    ????????}
    ????}
    }

    當代理對象生成后,最后由InvocationHandler的invoke()方法調用目標方法:在動態(tài)代理中InvocationHandler是核心,每個代理實例都具有一個關聯(lián)的調用處理程序(InvocationHandler)。對代理實例調用方法時,將對方法調用進行編碼并將其指派到它的調用處理程序(InvocationHandler)的invoke()方法。所以對代理方法的調用都是通InvocationHadler的invoke來實現(xiàn)中,而invoke方法根據(jù)傳入的代理對象,方法和參數(shù)來決定調用代理的哪個方法。具體方法簽名如下:

    invoke(Object?Proxy,Method?method,Object[]?args)

    從反編譯源碼分析調用invoke()過程:從反編譯后的源碼看$Proxy0類繼承了Proxy類,同時實現(xiàn)了Subject接口,即代理類接口,所以才能強制將代理對象轉換為Subject接口,然后調用$Proxy0中的request()方法。$Proxy0中request()源碼:

    ????//接口Subject中定義的放任request
    ????public?final?void?request()?throws??{
    ????????try?{
    ?????????????//h就是Proxy類中的變量protected?InvocationHandler?h;
    ????????????//this就是當前$Proxy0對象;
    ????????????//m3就是Class.forName("com.tian.swagger.proxy.Subject").getMethod("request",?new?Class[0]);
    ????????????//即是通過全路徑名,反射獲取的目標對象中的真實方法加參數(shù)
    ????????????//后面那個null是方法參數(shù),因為Subject的方法request參數(shù)為空
    ????????????super.h.invoke(this,?m3,?(Object[])null);
    ????????}?catch?(RuntimeException?|?Error?var2)?{
    ????????????throw?var2;
    ????????}?catch?(Throwable?var3)?{
    ????????????throw?new?UndeclaredThrowableException(var3);
    ????????}
    ????}

    所以成功的調到了InvocationHandler中的invoke()方法,但是invoke()方法在我們自定義的JDKDynamicProxy中實現(xiàn),JDKDynamicProxy中的invoke()方法:

    @Override
    public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
    ?????before();
    ?????Object?result?=?method.invoke(target,?args);
    ?????after();
    ?????return?result;
    }

    終于回到咱們寫的代碼里了。不容易吧,簡單JDK動態(tài)代理繞了這么大一圈。

    通過以上分析應該收獲:

    1,JDK動態(tài)代理只能代理接口,源碼里有判斷,不是接口就直接拋異常

    //校驗該類是否是接口類型,這里就是證明為什么JDK動態(tài)代理是代理接口的。
    ?if?(!interfaceClass.isInterface())?{
    ????throw?new?IllegalArgumentException(?interfaceClass.getName()?+?"?is?not?an?interface");
    ??}

    2,代理類是如何生成的

    3,代理類結構

    4,是如何調到自己定義的invoke方法的

    JDK動態(tài)代理源碼總結

    1,生成代理類$Proxy.class字節(jié)碼

    Proxy.newProxyInstance()-->getProxyClass()--->ProxyClassFactory.apply()--->ProxyGenerator.getGenerateProxyClass()

    2,調用Constructor.newInstance()實例化

    3,調用第2步生成的代理對象中的invoke方法,最后調到JDKDynamicProxy中的invoke方法,最后調到被代理對象的實現(xiàn)方法中

    通過以上的分析我們知道了JDK動態(tài)代理的使用和原理,也領略Java動態(tài)代理的強大之處,但是不難看出來使用JDK動態(tài)代理也有著它的局限性,JDK動態(tài)代理是在JVM內部動態(tài)的生成class字節(jié)碼對象(代理類.class),但是JDK動態(tài)代理只能針對接口進行操作,也就說它只適用于對接口的實現(xiàn)類去進行代理。因為有時候咱們希望對普通類進行代理,使用JDK動態(tài)代理就無法搞下去,于是另外一個強大的動態(tài)代理CGlib出現(xiàn)了,CGlib就能解決對普通類進行代理的問題。

    CGlib動態(tài)代理

    定義
    Byte?Code?Generation?Library?is?high?level?API?to?generate?and?transform?Java?byte?code.?It?is?used?by?AOP,?testing,?data?access?frameworks?to?generate?dynamic?proxy?objects?and?intercept?field?access.

    CGlib(Code Generation Library)是一個開源項目;是一個強大的,高性能,高質量的Code生成類庫,它可以在運行期擴展Java類與實現(xiàn)Java接口,它被AOP、測試、數(shù)據(jù)訪問框架用于生成動態(tài)代理對象和攔截字段訪問。

    CGlib包的底層是通過使用一個小而快的字節(jié)碼處理框架ASM,來轉換字節(jié)碼并生成新的類;CGlib是針對類來實現(xiàn)代理的,原理是對指定的業(yè)務類生成一個子類,并覆蓋其中業(yè)務方法實現(xiàn)代理;所以CGlib可以為無接口的類直接做代理,當然有接口的類也是可以的并無影響。

    pom

    <dependency>
    ?????<groupId>cglibgroupId>
    ?????<artifactId>cglibartifactId>
    ?????<version>3.2.12version>
    dependency>
    Java代碼
    public?class?TicketConcrete?{
    ????public?void?buy()?{
    ????????System.out.println("買到火車票了");
    ????}
    }
    public?class?CattlePersonMethodInterceptor?implements?MethodInterceptor?{
    ????@Override
    ????public?Object?intercept(Object?o,?Method?method,?Object[]?objects,?MethodProxy?methodProxy)?throws?Throwable?{
    ????????System.out.println("黃牛去買火車票");
    ????????Object?res?=?methodProxy.invokeSuper(o,?objects);
    ????????System.out.println("黃牛把火車票給老田");
    ????????return?res;
    ????}
    }
    public?class?Client?{
    ????public?static?void?main(String[]?args)?{
    ????????CattlePersonMethodInterceptor?cattlePerson?=?new?CattlePersonMethodInterceptor();
    ????????Enhancer?enhancer?=?new?Enhancer();
    ????????enhancer.setSuperclass(TicketConcrete.class);
    ????????enhancer.setCallback(cattlePerson);
    ????????TicketConcrete?person?=?(TicketConcrete)?enhancer.create();
    ????????person.buy();
    ????}
    }

    運行結果:

    黃牛去買火車票 買到火車票了 黃牛把火車票給老田

    上面已經(jīng)提到,CGlib庫是基于ASM的上層應用。對于代理沒有實現(xiàn)接口的類,CGlib非常實用。本質上來說,對于需要被代理的類,它只是動態(tài)生成一個子類以覆蓋非final的方法,同時綁定鉤子回調自定義的攔截器。值得說的是,它比JDK動態(tài)代理還要快。

    CGlib 部分源碼分析

    cglib.jar包目錄:

    本文只關心proxy目錄(CGlib動態(tài)代理相關主要類目錄).

    net.sf.cglib.
    ??????????? core:底層字節(jié)碼操作類;大部分與ASP相關。
    ??????????? transform:編譯期、運行期的class文件轉換類。
    ??????????? proxy:代理創(chuàng)建類、方法攔截類。
    ??????????? reflect:更快的反射類、C#風格的代理類。
    ??????????? util:集合排序工具類
    ??????????? beans:JavaBean相關的工具類

    源碼從enhancer.create()開始看,因為這里返回的Object直接強轉為TicketConcrete(被代理的類)

    Enhancer類中

    //這里沒有具體什么
    public?Object?create()?{
    ???????classOnly?=?false;
    ???????argumentTypes?=?null;
    ???????return?createHelper();
    }
    //構建幫助器
    private?Object?createHelper()?{
    ???//提前驗證回調類型和filter
    ???preValidate();
    ????//生成key
    ???Object?key?=?KEY_FACTORY.newInstance((superclass?!=?null)???superclass.getName()?:?null,
    ??????????ReflectUtils.getNames(interfaces),
    ??????????filter?==?ALL_ZERO???null?:?new?WeakCacheKey(filter),
    ??????????callbackTypes,
    ??????????useFactory,
    ??????????interceptDuringConstruction,
    ??????????serialVersionUID);
    ????//復制個當前key
    ????this.currentKey?=?key;
    ????//使用key構建代理對象,個人認為這里代碼其實可以直接寫成?return?super.create(key);
    ????//看得出來這里的create方法很關鍵,AbstractClassGenerator.create()
    ????Object?result?=?super.create(key);
    ????return?result;
    }

    AbstractClassGenerator類中

    private?static?volatile?Map?CACHE?=?new?WeakHashMap();
    protected?Object?create(Object?key)?{
    ????????try?{
    ????????????//獲取類加載器AppClassLoader
    ????????????ClassLoader?loader?=?getClassLoader();
    ????????????//這里和jdk動態(tài)代理類似,也用到了緩存,classloader為key
    ????????????Map?cache?=?CACHE;
    ????????????ClassLoaderData?data?=?cache.get(loader);
    ????????????//data==null
    ????????????if?(data?==?null)?{
    ????????????????synchronized?(AbstractClassGenerator.class)?{
    ????????????????????cache?=?CACHE;
    ????????????????????data?=?cache.get(loader);
    ????????????????????//data==null
    ????????????????????if?(data?==?null)?{
    ????????????????????????//沒有數(shù)據(jù)則構建新緩存
    ????????????????????????Map?newCache?=?new?????????????????????????
    ?????????????????????????????WeakHashMap(cache);
    ????????????????????????//關鍵點:這里生成了class并存入緩存
    ????????????????????????data?=?new?ClassLoaderData(loader);
    ????????????????????????newCache.put(loader,?data);
    ????????????????????????CACHE?=?newCache;
    ????????????????????}
    ????????????????}
    ????????????}
    ????????????this.key?=?key;
    ????????????//從緩存中取數(shù)據(jù)
    ????????????Object?obj?=?data.get(this,?getUseCache());
    ????????????//根據(jù)返回類型不同,然后進行對應的實例化
    ????????????if?(obj?instanceof?Class)?{
    ????????????????return?firstInstance((Class)?obj);
    ????????????}
    ????????????return?nextInstance(obj);
    ????????}?catch?(RuntimeException?e|Error?e|Exception?e)?{
    ????????????throw?new?CodeGenerationException(e);
    ????????}
    ????}
    ??public?ClassLoaderData(ClassLoader?classLoader)?{
    ???????????//類加載器是否存在
    ????????????if?(classLoader?==?null)?{
    ????????????????throw?new?IllegalArgumentException("classLoader?==?null?is?not?yet?supported");
    ????????????}
    ????????????//?弱引用也是用來描述非必需對象的,當JVM進行垃圾回收時,
    ????????????//WeakReference無論內存是否充足,都會回收被弱引用關聯(lián)的對象
    ????????????this.classLoader?=?new?WeakReference(classLoader);
    ????????????Function?load?=
    ????????????????????new?Function()?{
    ????????????????????????public?Object?apply(AbstractClassGenerator?gen)?{
    ????????????????????????????//生成Class對象
    ????????????????????????????Class?klass?=?gen.generate(ClassLoaderData.this);
    ????????????????????????????return?gen.wrapCachedClass(klass);
    ????????????????????????}
    ????????????????????};
    ????????????generatedClasses?=?new?LoadingCache(GET_KEY,?
    ????????????????????????????????????????????????????????????????????????????????????????load);
    ????????}
    //生成Class對象
    protected?Class?generate(ClassLoaderData?data)?{
    ????????Class?gen;
    ????????Object?save?=?CURRENT.get();
    ????????CURRENT.set(this);
    ????????try?{
    ????????????ClassLoader?classLoader?=?data.getClassLoader();
    ????????????if?(classLoader?==?null)?{
    ?????????????throw?new?IllegalStateException("ClassLoader?is?null?while?trying?to?define?class?"?+?getClassName()?+?".?It?seems?that?the?loader?has?been?expired?from?a?weak?reference?somehow.?"?+??"Please?file?an?issue?at?cglib's?issue?tracker.");
    ????????????}
    ????????????synchronized?(classLoader)?{
    ??????????????//生成代理類的名字
    ??????????????//com.tian.swagger.proxy.cglib.TicketConcrete$$FastClassByCGLIB$$56a92ac1
    ??????????????//三部分組成:被代理對象的類全名+FastClassBYCGLIB+hashcode
    ??????????????String?name?=?generateClassName(data.getUniqueNamePredicate());??????????????
    ??????????????data.reserveName(name);
    ??????????????this.setClassName(name);
    ????????????}
    ????????????if?(attemptLoad)?{
    ????????????????try?{
    ????????????????????gen?=?classLoader.loadClass(getClassName());
    ????????????????????return?gen;
    ????????????????}?catch?(ClassNotFoundException?e)?{
    ????????????????????//?ignore
    ????????????????}
    ????????????}
    ????????????//根據(jù)策略生成不同的字節(jié)碼
    ????????????byte[]?b?=?strategy.generate(this);
    ????????????String?className?=?ClassNameReader.getClassName(new?ClassReader(b));
    ????????????ProtectionDomain?protectionDomain?=?getProtectionDomain();
    ????????????//利用反射生成class對象
    ????????????synchronized?(classLoader)?{?//?just?in?case
    ????????????????if?(protectionDomain?==?null)?{
    ????????????????????gen?=?ReflectUtils.defineClass(className,?b,?classLoader);
    ????????????????}?else?{
    ????????????????????gen?=?ReflectUtils.defineClass(className,?b,?classLoader,?protectionDomain);
    ????????????????}
    ????????????}
    ????????????return?gen;
    ????????}?catch?(RuntimeException?e|Error?e)?{
    ????????????throw?e;
    ????????}?catch?(Exception?e)?{
    ????????????throw?new?CodeGenerationException(e);
    ????????}?finally?{
    ????????????CURRENT.set(save);
    ????????}
    ????}
    //生成字節(jié)碼
    public?byte[]?generate(ClassGenerator?cg)?throws?Exception?{
    ????????DebuggingClassWriter?cw?=?getClassVisitor();
    ????????transform(cg).generateClass(cw);
    ????????return?transform(cw.toByteArray());
    }
    protected?ClassGenerator?transform(ClassGenerator?cg)?throws?Exception?{
    ????return?cg;
    }
    public?interface?ClassGenerator?{
    ????void?generateClass(ClassVisitor?v)?throws?Exception;
    }

    ClassGenerator實現(xiàn)類AbstractClassGeneratorAbstractClassGenerator的子類主要有

    Enhancer
    FastClass.Generator
    KeyFactory.Generator

    把生成的class文件保存,會生成以上三個class文件

    public?class?Client?{
    ????public?static?void?main(String[]?args)?{
    ????????//把生成的class文件保存,會生成以上三個class文件
    ????????System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,?"D:\\");
    ????????long?s=System.currentTimeMillis();
    ????????CattlePersonMethodInterceptor?cattlePerson?=?new?CattlePersonMethodInterceptor();
    ????????Enhancer?enhancer?=?new?Enhancer();
    ????????enhancer.setSuperclass(TicketConcrete.class);
    ????????enhancer.setCallback(cattlePerson);
    ????????TicketConcrete?person?=?(TicketConcrete)?enhancer.create();
    ????????person.buy();
    ????????System.out.println(System.currentTimeMillis()-s);
    ????}
    }

    最終生成相關的三個Class

    com.tian.swagger.proxy.cglib.TicketConcrete$$EnhancerByCGLIB$$e39d634f
    com.tian.swagger.proxy.cglib.TicketConcrete$$FastClassByCGLIB$$56a92ac1
    com.tian.swagger.proxy.cglib.TicketConcrete$$EnhancerByCGLIB$$e39d634f$$FastClassByCGLIB$$ba31b84d

    重點關注TicketConcrete$$EnhancerByCGLIB$$e39d634f文件中主要內容:

    package?com.tian.swagger.proxy.cglib;

    import?java.lang.reflect.Method;
    import?net.sf.cglib.core.ReflectUtils;
    import?net.sf.cglib.core.Signature;
    import?net.sf.cglib.proxy.Callback;
    import?net.sf.cglib.proxy.Factory;
    import?net.sf.cglib.proxy.MethodInterceptor;
    import?net.sf.cglib.proxy.MethodProxy;
    //繼承了TicketConcrete,證明了CGlib是針對被代理類進行生成一個子類來實現(xiàn)代理的
    //和jdk動態(tài)代理不一樣,動態(tài)代理是集成Proxy類實現(xiàn)了我們的接口,
    //而cglib是集成了我們的類重寫了父類中的方法
    public?class?TicketConcrete$$EnhancerByCGLIB$$e39d634f?
    ???????extends?TicketConcrete
    ???????implements?Factory?
    {
    ????private?boolean?CGLIB$BOUND;
    ????public?static?Object?CGLIB$FACTORY_DATA;
    ????private?static?final?ThreadLocal?CGLIB$THREAD_CALLBACKS;
    ????private?static?final?Callback[]?CGLIB$STATIC_CALLBACKS;
    ????private?MethodInterceptor?CGLIB$CALLBACK_0;
    ????private?static?Object?CGLIB$CALLBACK_FILTER;
    ????private?static?final?Method?CGLIB$buy$0$Method;
    ????private?static?final?MethodProxy?CGLIB$buy$0$Proxy;
    ????private?static?final?Object[]?CGLIB$emptyArgs;
    ????private?static?final?Method?CGLIB$equals$1$Method;
    ????private?static?final?MethodProxy?CGLIB$equals$1$Proxy;
    ????private?static?final?Method?CGLIB$toString$2$Method;
    ????private?static?final?MethodProxy?CGLIB$toString$2$Proxy;
    ????private?static?final?Method?CGLIB$hashCode$3$Method;
    ????private?static?final?MethodProxy?CGLIB$hashCode$3$Proxy;
    ????private?static?final?Method?CGLIB$clone$4$Method;
    ????private?static?final?MethodProxy?CGLIB$clone$4$Proxy;

    ????static?void?CGLIB$STATICHOOK1()?{
    ????????CGLIB$THREAD_CALLBACKS?=?new?ThreadLocal();
    ????????CGLIB$emptyArgs?=?new?Object[0];
    ????????//var0為代理對象com.tian.swagger.proxy.cglib.TicketConcrete
    ????????Class?var0?=?Class.forName("com.tian.swagger.proxy.cglib.TicketConcrete$$EnhancerByCGLIB$$e39d634f");
    ????????Class?var1;
    ????????Method[]?var10000?=?ReflectUtils.findMethods(new?String[]{"equals",?"(Ljava/lang/Object;)Z",?"toString",?"()Ljava/lang/String;",?"hashCode",?"()I",?"clone",?"()Ljava/lang/Object;"},?(var1?=?Class.forName("java.lang.Object")).getDeclaredMethods());
    ????????CGLIB$equals$1$Method?=?var10000[0];
    ????????CGLIB$equals$1$Proxy?=?MethodProxy.create(var1,?var0,?"(Ljava/lang/Object;)Z",?"equals",?"CGLIB$equals$1");
    ????????CGLIB$toString$2$Method?=?var10000[1];
    ????????CGLIB$toString$2$Proxy?=?MethodProxy.create(var1,?var0,?"()Ljava/lang/String;",?"toString",?"CGLIB$toString$2");
    ????????CGLIB$hashCode$3$Method?=?var10000[2];
    ????????CGLIB$hashCode$3$Proxy?=?MethodProxy.create(var1,?var0,?"()I",?"hashCode",?"CGLIB$hashCode$3");
    ????????CGLIB$clone$4$Method?=?var10000[3];
    ????????CGLIB$clone$4$Proxy?=?MethodProxy.create(var1,?var0,?"()Ljava/lang/Object;",?"clone",?"CGLIB$clone$4");
    ????????//被代理的方法被代理方法buy()
    ????????CGLIB$buy$0$Method?=?ReflectUtils.findMethods(new?String[]{"buy",?"()V"},?(var1?=?Class.forName("com.tian.swagger.proxy.cglib.TicketConcrete")).getDeclaredMethods())[0];
    ????????//代理方法
    ????????CGLIB$buy$0$Proxy?=?MethodProxy.create(var1,?var0,?"()V",?"buy",?"CGLIB$buy$0");
    ????}
    ????//這個方法在fastClass中調用,此調用是執(zhí)行真正的邏輯
    ????final?void?CGLIB$buy$0()?{
    ????????super.buy();
    ????}
    ????//方法重寫
    ????public?final?void?buy()?{
    ????????//判斷目標類是否有設置回調:enhancer.setCallback(this);
    ????????MethodInterceptor?var10000?=?this.CGLIB$CALLBACK_0;
    ????????if?(this.CGLIB$CALLBACK_0?==?null)?{
    ????????????CGLIB$BIND_CALLBACKS(this);
    ????????????var10000?=?this.CGLIB$CALLBACK_0;
    ????????}
    ????????//設置了方法的回調則調用攔截器方法intercept
    ????????if?(var10000?!=?null)?{
    ????????????var10000.intercept(this,?CGLIB$buy$0$Method,?CGLIB$emptyArgs,?CGLIB$buy$0$Proxy);
    ????????}?else?{
    ????????????super.buy();
    ????????}
    ????}
    ????public?static?MethodProxy?CGLIB$findMethodProxy(Signature?var0)?{
    ????????//省略相關的代碼
    ????}
    ????//省略調equals?hashCode?clone等方法??
    }
    CGlib總結
    • 首先生成代理對象?!緞?chuàng)建增強類enhancer,設置代理類的父類,設置回調攔截方法,返回創(chuàng)建的代理對象】
    • 調用代理類中的方法。【這里調用的代理類中的方法實際上是重寫的父類的攔截。重寫的方法中會去調用intercept方法】
    • 調用intercept,方法中會對調用代理方法中的invokeSuper方法。而在invokeSuper中維護了一個FastClassInfo類,其包含四個屬性字段,分別為FastClass f1(目標類);FastClass f2 (代理類); int i1(目標類要執(zhí)行方法的下標); int i2(代理類要執(zhí)行方法的下標); invokeSuper中會調用的為代理類中的對應方法(代理類繼承父類的時候,對于其父類的方法,自己會生成兩個方法,一個是重寫的方法,一個是代理生成的方法,這里調用的即是代理生成的方法)
    • 調用代理類中的代理方法,代理方法中通過super.method來真正地調用要執(zhí)行被代理類的方法。

    JDK VS CGlib

    JDK動態(tài)代理只能夠對接口進行代理,不能對普通的類進行代理(因為所有生成的代理類的父類為Proxy,Java類繼承機制不允許多重繼承);CGLIB能夠代理普通類;JDK動態(tài)代理使用Java原生的反射API進行操作,在生成類上比較高效;CGLIB使用ASM框架直接對字節(jié)碼進行操作,在類的執(zhí)行過程中比較高效。

    動態(tài)代理典型場景的應用

    Mybatis中的使用場景

    Mybatis應該算持久化框架中使用率最多之一,細心的小伙伴有沒有發(fā)現(xiàn),比如我們在使用UserInfoMapper.java是接口,接口不是能實例化的,那為什么我們還能注入呢?

    ????@Resource
    ????private?UserInfoMapper?userInfoMapper;

    為什么還能userMapper.insert()這么使用呢?先看看Mybatis沒有集成Spring是如何實例化的。

    <bean?id="userMapper"?class="org.mybatis.spring.mapper.MapperFactoryBean">
    ??<property?name="mapperInterface"?value="org.mybatis.spring.sample.mapper.UserMapper"?/>
    ??<property?name="sqlSessionFactory"?ref="sqlSessionFactory"?/>
    bean>

    上面的MapperFactoryBean就可以實例化出mapperInterface類型的Bean,因為MapperFactoryBean實現(xiàn)了FactoryBean接口的getObject方法,可以實例化出我們想要的Bean,實際上是通過Jdk動態(tài)代理得到的Bean。

    public?T?getObject()?throws?Exception?{
    ????return?getSqlSession().getMapper(this.mapperInterface);
    }

    調用方式:

    SqlSession?session?=?sqlSessionFactory.openSession();??
    try?{??
    ??User?user=?(User)?session.selectOne("org.mybatis.example.UserMapper.selectBlog",?1);??
    }?finally?{??
    ??session.close();??
    }??

    上面的UserMapper接口是一個個的配置來實例化的,每次需要openSession,close Session,重復工作太多,這樣肯定比較麻煩,下面看看項目中常用的Mybatis與Spring集成的配置方式。

    "sqlSessionFactory"?class="org.mybatis.spring.SqlSessionFactoryBean">
    ?"mapperLocations"?value="classpath*:com/**/mapper/**/*Mapper*.xml"?/>
    ?????"dataSource"?ref="dataSource"?/>

    這個SqlSessionFactoryBean會做哪些事呢,主要是把Mapper.xml文件與Mapper.java加載進來,根據(jù)namespace加載對應的接口類到MapperRegistry,把方法名與Mapper.xml里的Select id對應起來等等。MapperRegistry相當于是一個緩存,后面創(chuàng)建代理對象是會用到。

    <bean?class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    ?<property?name="annotationClass"?value="javax.annotation.Autowire">property>?
    ?????<property?name="basePackage"?value="com.***.mapper"?/>
    ?????<property?name="sqlSessionFactoryBeanName"?value="sqlSessionFactory"/>
    bean>

    這里的值設置,只要是注解就行,不一定是Autowird,它代表只對有注解的接口類創(chuàng)建代理對象,否則,對basePackage下所有接口創(chuàng)建代理對象。

    MapperScannerConfigurer可以對basePackage下所有Mapper接口創(chuàng)建代理對象,而不是像上面一個個配置。因為他實現(xiàn)了接口BeanDefinitionRegistryPostProcessor,它就是用來自定義Bean的,看看源碼它是如何實現(xiàn)的。

    上面設置mapperInterface,BeanClass其實就是上面講的Mybatis沒有集成Spring的配置方式。

    我們知道了代理對象是通過MapperFactoryBean創(chuàng)建的。具體看看是如何創(chuàng)建的。

    根據(jù)接口類創(chuàng)建代理對象:

    /**
    ???*?{@inheritDoc}
    ???*/

    ??public?T?getObject()?throws?Exception?{
    ????return?getSqlSession().getMapper(this.mapperInterface);
    ??}

    這里就用到了之前說的MapperRegistry,只是用來校驗該接口是否存在。這里又用到了MapperProxy,是一個代理類,實現(xiàn)了InvocationHandler接口。

    ?public?static??T?newMapperProxy(Class?mapperInterface,?SqlSession?sqlSession)?{
    ????ClassLoader?classLoader?=?mapperInterface.getClassLoader();
    ????Class[]?interfaces?=?new?Class[]{mapperInterface};
    ????MapperProxy?proxy?=?new?MapperProxy(sqlSession);
    ????return?(T)?Proxy.newProxyInstance(classLoader,?interfaces,?proxy);
    ??}
    public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
    ????if?(method.getDeclaringClass()?==?Object.class)?{
    ??????return?method.invoke(this,?args);
    ????}
    ????final?Class?declaringInterface?=?findDeclaringInterface(proxy,?method);
    ????final?MapperMethod?mapperMethod?=?new?MapperMethod(declaringInterface,?method,?sqlSession);
    ????final?Object?result?=?mapperMethod.execute(args);
    ????if?(result?==?null?&&?method.getReturnType().isPrimitive()?&&?!method.getReturnType().equals(Void.TYPE))?{
    ??????throw?new?BindingException("Mapper?method?'"?+?method.getName()?+?"'?("?+?method.getDeclaringClass()?+?")?attempted?to?return?null?from?a?method?with?a?primitive?return?type?("?+?method.getReturnType()?+?").");
    ????}
    ????return?result;
    ??}

    當我們調用Mpper接口方法時,會被MapperProxy代理類攔截調用,主要調用實現(xiàn)就是mapperMethod.execute(args),看看底層實現(xiàn)。

    public?Object?execute(Object[]?args)?{
    ????Object?result?=?null;
    ????if?(SqlCommandType.INSERT?==?type)?{
    ??????Object?param?=?getParam(args);
    ??????result?=?sqlSession.insert(commandName,?param);
    ????}?else?if?(SqlCommandType.UPDATE?==?type)?{
    ??????Object?param?=?getParam(args);
    ??????result?=?sqlSession.update(commandName,?param);
    ????}?else?if?(SqlCommandType.DELETE?==?type)?{
    ??????Object?param?=?getParam(args);
    ??????result?=?sqlSession.delete(commandName,?param);
    ????}?else?if?(SqlCommandType.SELECT?==?type)?{
    ??????if?(returnsVoid?&&?resultHandlerIndex?!=?null)?{
    ????????executeWithResultHandler(args);
    ??????}?else?if?(returnsMany)?{
    ????????result?=?executeForMany(args);
    ??????}?else?if?(returnsMap)?{
    ????????result?=?executeForMap(args);
    ??????}?else?{
    ????????Object?param?=?getParam(args);
    ????????result?=?sqlSession.selectOne(commandName,?param);?//跟上面沒有集成Spring調用方式一樣的。
    ??????}
    ????}?else?{
    ??????throw?new?BindingException("Unknown?execution?method?for:?"?+?commandName);
    ????}
    ????return?result;
    ??}

    以上就是代理模式、動態(tài)代理(JDK、CGLib)、MyBatis中的動態(tài)代理的使用。

    好了,就這么多了。



    推薦閱讀:

    MySQL一共可以創(chuàng)建多少張表

    出乎意料,刷新認知:讀者朋友們藏龍臥虎



    歡迎關注微信公眾號:互聯(lián)網(wǎng)全棧架構,收取更多有價值的信息。


    瀏覽 46
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

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

    手機掃一掃分享

    分享
    舉報

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    人妻精品在线 | 色欲亚洲 | 美女干逼免费的 | 狼人香蕉影院 | 天天添夜夜添免费视频 |