最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java設(shè)計模式之動態(tài)代理

 更新時間:2019年01月16日 09:03:35   作者:Haozz_1994  
今天小編就為大家分享一篇關(guān)于Java設(shè)計模式之動態(tài)代理,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧

動態(tài)代理的意義在于生成一個占位(又稱代理對象),來代理真實對象,從而控制真實對象的訪問。

我們首先來談?wù)勈裁词谴砟J?。假設(shè)客戶帶著需求去找公司,顯然不會直接和軟件工程師談,而是和商務(wù)談,此時客戶會認(rèn)為商務(wù)就代表公司,客戶是通過商務(wù)去訪問軟件工程師的。我們就可以認(rèn)為商務(wù)(代理對象)代理了軟件工程師(真實對象),因此,代理的作用就是,在真實對象訪問之前或者之后加入對應(yīng)的邏輯,或者根據(jù)其他規(guī)則控制是否使用真實對象。

商務(wù)和軟件工程師是代理和被代理的關(guān)系,客戶是通過商務(wù)去訪問軟件工程師的。此時客戶就是程序中的調(diào)用者,商務(wù)就是代理對象,軟件工程師就是真實對象。我們需要在調(diào)用者調(diào)用對象之前產(chǎn)生一個代理對象,而這個代理對象需要和真實對象建立代理關(guān)系,所以代理必須分為兩個步驟:

  • 代理對象和真實對象建立代理關(guān)系
  • 實現(xiàn)代理對象的代理邏輯方法

在Java中有多種動態(tài)代理技術(shù),比如JDK、CGLIB、Javassist、ASM,其中最常用的動態(tài)代理技術(shù)有兩種:一種是JDK動態(tài)代理,這是JDK自帶的功能;另一種是CGLIB,這是第三方提供的一個技術(shù)。目前,Spring常用JDK和CGLIB,而MyBatis還使用了Javassist。(CGLIB的底層原理是ASM)

本文只討論兩種最常用的動態(tài)代理技術(shù):JDK和CGLIB。在JDk動態(tài)代理中必須使用接口,而CGLIB不需要,所以使用CGLIB會更簡單一點。下面依次討論這兩種技術(shù)。

一.JDK動態(tài)代理

JDK動態(tài)代理是java.lang.reflect.*包提供的方式。它必須借助一個接口才能產(chǎn)生代理對象,所以先定義接口HelloWorld:

public interface HelloWorld {
  void sayHelloWorld();
}

然后提供實現(xiàn)類HelloWorldImpl來實現(xiàn)接口:

public class HelloWorldImpl implements HelloWorld {
  @Override
  public void sayHelloWorld(){
    System.out.println("Hello World");
  }
}

這是最簡答的Java接口和實現(xiàn)類的關(guān)系,此時可以開始動態(tài)代理了。按照我們之前的分析,先要建立代理對象和真實對象的關(guān)系,然后實現(xiàn)代理邏輯,所以一共分為兩個步驟。在JDK動態(tài)代理中,代理邏輯類必須去實現(xiàn)java.lang.reflect.InvocationHandler接口,它定義了一個invoke方法,并提供接口數(shù)組用于下掛代理對象。我們自己定義一個JDK動態(tài)代理的類:

public class MyJdkProxyExample implements InvocationHandler{
  /**
   * @description 真實對象
   **/
  private Object target = null;
  /**
   * @author haozz
   * @date 2018-05-21 10:54
   * @param target 真實對象
   * @return 代理對象
   * @throws
   * @description 建立代理對象和真實對象的代理關(guān)系,并返回代理對象
   **/
  public Object getProxy(Object target) {
    this.target = target;
    return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
  }
  /**
   * @author haozz
   * @date 2018-05-21 11:09
   * @param proxy 代理對象
   * @param method 當(dāng)前調(diào)度方法
   * @param args 當(dāng)前方法參數(shù)
   * @return 代理結(jié)果
   * @throws Throwable 異常
   * @description 代理方法邏輯
   **/
  @Override
  public Object invoke(Object proxy, Method method,Object [] args) throws Throwable{
    System.out.println("進(jìn)入代理邏輯方法");
    System.out.println("在調(diào)度真實對象之前的服務(wù)");
    Object obj = method.invoke(target,args);//相當(dāng)于調(diào)用sayHelloWorld方法
    System.out.println("在調(diào)度真實對象之后的服務(wù)");
    return obj;
  }
}

第一步,通過getProxy()方法建立代理對象和真實對象的關(guān)系,并返回代理對象。首先用類的屬性target保存了真實對象,然后通過Proxy.newProxyInstance()方法建立并生成代理對象,該方法中包含3個參數(shù):

  • 第1個是類加載器,這里采用了target本身的類加載器;
  • 第2個是把生成的動態(tài)代理對象下掛在哪些接口下,這個寫法就是放在target實現(xiàn)的接口下。HelloWorldImpl對象的接口顯然就是HelloWorld,代理對象可以這樣聲明:HelloWorld proxy = xxxx;;
  • 第3個是定義實現(xiàn)方法邏輯的代理類,this表示當(dāng)前對象,它必須實現(xiàn)InvocationHandler接口的invoke方法,它就是代理邏輯方法的現(xiàn)實方法。

第二步,通過invoke方法實現(xiàn)代理邏輯方法。invoke方法的3個參數(shù):

  • proxy,代理對象,就是getProxy方法生成的對象;
  • method,當(dāng)前調(diào)度的方法;
  • args,當(dāng)前調(diào)度方法的參數(shù)。

當(dāng)我們使用了代理對象調(diào)度方法后,它就會進(jìn)入到invoke方法里面。

Object obj = method.invoke(target,args);

這行代碼相當(dāng)于調(diào)度真實對象的方法,只是通過反射實現(xiàn)。類比前面的例子,proxy相當(dāng)于商務(wù),target相當(dāng)于軟件工程師,getProxy方法就是建立商務(wù)和軟件工程師之間的代理關(guān)系,invoke方法就是商務(wù)邏輯。

測試JDK動態(tài)代理:

  @Test
  public void testJdkProxy(){
    MyJdkProxyExample jdkProxy = new MyJdkProxyExample();
    //綁定關(guān)系,因為掛在接口HelloWorld下,所以聲明代理對象HelloWorld proxy
    HelloWorld proxy = (HelloWorld) jdkProxy.getProxy(new HelloWorldImpl());
    //此時HelloWorld對象已經(jīng)是一個代理對象,它會進(jìn)入代理的邏輯方法invoke里
    proxy.sayHelloWorld();
  }

首先通過getProxy方法綁定了代理關(guān)系,然后在代理對象調(diào)度sayHelloWorld方法時進(jìn)入了代理的邏輯,測試結(jié)果如下:

進(jìn)入代理邏輯方法
在調(diào)度真實對象之前的服務(wù)
Hello World
在調(diào)度真實對象之后的服務(wù)

此時,在調(diào)度打印Hello World之前和之后都可以加入相關(guān)的邏輯,甚至可以不調(diào)度Hello World的打印。

個人小結(jié):JDK動態(tài)代理要求真實對象和代理對象之間是實現(xiàn)類和接口的關(guān)系,創(chuàng)建一個代理邏輯類實現(xiàn)InvocationHandler接口,其中g(shù)etProxy方法用于生成代理對象,然后重寫invoke方法。

二.CGLIB動態(tài)代理

JDK動態(tài)代理必須提供接口才能使用,在一些不能提供接口的環(huán)境中,只能采用其他第三方技術(shù),比如CGLIB動態(tài)代理,這里提供CGLIB動態(tài)代理的相關(guān)jar包,供學(xué)習(xí)和測試使用。它的優(yōu)勢在于不需要提供接口,只要一個非抽象類就能實現(xiàn)動態(tài)代理。

CGLIB原理:動態(tài)生成一個要代理類的子類,子類重寫要代理的類的所有不是final的方法。在子類中采用方法攔截的技術(shù)攔截所有父類方法的調(diào)用,順勢織入橫切邏輯。它比使用java反射的JDK動態(tài)代理要快。CGLIB底層:使用字節(jié)碼處理框架ASM,來轉(zhuǎn)換字節(jié)碼并生成新的類。不鼓勵直接使用ASM,因為它要求你必須對JVM內(nèi)部結(jié)構(gòu)包括class文件的格式和指令集都很熟悉。CGLIB缺點:對于final方法,無法進(jìn)行代理。CGLIB廣泛地被許多AOP的框架使用,例如Spring AOP和dynaop。Hibernate使用CGLIB來代理單端single-ended(多對一和一對一)關(guān)聯(lián)。

我們以下面這個類為例:

public class ReflectServiceImpl {
  public void sayHello(String name){
    System.out.println("Hello "+name);
  }
}

它不存在實現(xiàn)任何接口,所以不能使用JDK動態(tài)代理,這里采用CGLIB動態(tài)代理技術(shù):

public class MyCglibProxyExample implements MethodInterceptor{
  /**
   * @author haozz
   * @date 2018-05-21 15:11
   * @param cls Class類
   * @return Class類的CGLIB對象
   * @throws
   * @description 生成CGLIB代理對象
   **/
  public Object getProxy(Class cls){
    //CGLIB增強(qiáng)類對象
    Enhancer enhancer = new Enhancer();
    //設(shè)置增強(qiáng)類型
    enhancer.setSuperclass(cls);
    //定義代理邏輯對象為當(dāng)前對象,要求當(dāng)前對象實現(xiàn)MethodInterceptor方法
    enhancer.setCallback(this);
    //生成并返回代理對象
    return enhancer.create();
  }
  /**
   * @author haozz
   * @date 2018-05-21 15:17
   * @param proxy 代理對象
   * @param method 目標(biāo)方法
   * @param args 目標(biāo)方法參數(shù)
   * @param methodProxy 方法代理
   * @return 代理邏輯返回
   * @throws Throwable 異常
   * @description 代理邏輯方法
   **/
  @Override
  public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)throws Throwable{
    System.out.println("調(diào)用真實對象前");
    //CGLIB反射調(diào)用真實對象方法
    Object result = methodProxy.invokeSuper(proxy,args);
    System.out.println("調(diào)用真實對象后");
    return result;
  }
}

這里用了CGLIB的加強(qiáng)者Enhancer(net.sf.sglib.proxy.Enhancer),通過設(shè)置超類的方法(setSuperclass),然后通過setCallback方法設(shè)置哪個類為它的代理類。其中,參數(shù)this表示當(dāng)前對象,那就要求用this這個對象實現(xiàn)接口MethodInterceptor(net.sf.sglib.proxy.MethodInterceptor)的方法intercept,然后返回代理對象。那么此時當(dāng)前類的intercept方法就是其代理邏輯方法,其參數(shù)內(nèi)容見代碼注解,我們在反射真實對象方法前后進(jìn)行了打印,CGLIB是通過如下代碼完成的:

Object result = methodProxy.invokeSuper(proxy,args);

測試一下CGLIB動態(tài)代理:

  @Test
  public void testCglibProxy(){
    MyCglibProxyExample cglibProxy = new MyCglibProxyExample();
    ReflectServiceImpl obj = (ReflectServiceImpl) cglibProxy.getProxy(ReflectServiceImpl.class);
    obj.sayHello("haozz");
  }

得到結(jié)果:

調(diào)用真實對象前
Hello haozz
調(diào)用真實對象后

個人小結(jié):CGLIB動態(tài)代理相對簡單一些,通過字節(jié)碼技術(shù)為需要代理的類生成一個子類,它不需要提供接口,只需寫一個代理邏輯類并實現(xiàn)MethodInterceptor接口,其中g(shù)etProxy方法用于生成代理對象,然后重寫intercept方法即可。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請查看下面相關(guān)鏈接

相關(guān)文章

  • SpringBoot項目如何將Bean注入到普通類中

    SpringBoot項目如何將Bean注入到普通類中

    這篇文章主要介紹了SpringBoot項目如何將Bean注入到普通類中,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 一文詳解Spring?中的順序問題

    一文詳解Spring?中的順序問題

    本文主要介紹了Spring?中的順序問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • SpringBoot 中使用JSP的方法示例

    SpringBoot 中使用JSP的方法示例

    本篇文章主要介紹了SpringBoot 中使用JSP的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-06-06
  • Java實戰(zhàn)權(quán)限管理系統(tǒng)的實現(xiàn)流程

    Java實戰(zhàn)權(quán)限管理系統(tǒng)的實現(xiàn)流程

    讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SpringBoot+MyBatis+AOP+LayUI+Mysql實現(xiàn)一個權(quán)限管理系統(tǒng),大家可以在過程中查缺補(bǔ)漏,提升水平
    2022-01-01
  • InputStreamReader 和FileReader的區(qū)別及InputStream和Reader的區(qū)別

    InputStreamReader 和FileReader的區(qū)別及InputStream和Reader的區(qū)別

    這篇文章主要介紹了InputStreamReader 和FileReader的區(qū)別及InputStream和Reader的區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2015-12-12
  • 簡單理解Java的抽象類

    簡單理解Java的抽象類

    這篇文章主要介紹了Java的抽象類,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-09-09
  • java如何對map進(jìn)行排序詳解(map集合的使用)

    java如何對map進(jìn)行排序詳解(map集合的使用)

    這篇文章主要介紹了java如何對map進(jìn)行排序,java map集合的使用詳解,大家可以參考使用
    2013-12-12
  • 詳解Java中Iterable與Iterator用法

    詳解Java中Iterable與Iterator用法

    在本文中小編給大家分享了關(guān)于Java中Iterable與Iterator的用法知識點內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。
    2018-10-10
  • Java結(jié)構(gòu)型設(shè)計模式中建造者模式示例詳解

    Java結(jié)構(gòu)型設(shè)計模式中建造者模式示例詳解

    建造者模式,是一種對象構(gòu)建模式 它可以將復(fù)雜對象的建造過程抽象出來,使這個抽象過程的不同實現(xiàn)方法可以構(gòu)造出不同表現(xiàn)的對象。本文將通過示例講解建造者模式,需要的可以參考一下
    2022-09-09
  • SpringBoot?Loki安裝簡介及實戰(zhàn)思路

    SpringBoot?Loki安裝簡介及實戰(zhàn)思路

    這篇文章主要為大家介紹了SpringBoot?Loki安裝簡介及實戰(zhàn)思路詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪的相關(guān)資料
    2022-11-11

最新評論

涞源县| 凌源市| 翁源县| 将乐县| 靖西县| 靖江市| 常德市| 阿图什市| 天祝| 民和| 长葛市| 元谋县| 乃东县| 禄丰县| 汪清县| 塘沽区| 吐鲁番市| 天祝| 宝坻区| 赫章县| 江津市| 赤峰市| 筠连县| 东丰县| 德清县| 汝城县| 泾阳县| 突泉县| 昌乐县| 莒南县| 丰原市| 阳泉市| 托里县| 乌拉特后旗| 文安县| 那曲县| 高唐县| 哈密市| 珲春市| 登封市| 和田县|