首頁>科技>

今天講一個比較深層的知識點:JDK動態代理,這是個可以讓小白在大咖面前裝B的神器,順便送你一個代理模式的溫習機會。

代理模式場景

為了引出動態代理的用法,我們先看看代理設計模式,這能讓你了解JDK動態代理的應用場景的同時,讓你記憶深刻。代理模式是通過代理物件作為中間人來訪問目標物件,這樣可以完美的遵循開閉原則,從而避免修改目標物件來滿足需求而降低可維護性。

現實生活中比較常見的各種中間商、經紀人都是活生生的代理模式例子。咱們拿明星經紀人來說

上述關係現實中還是非常複雜的,比如什麼乾爹啦、劈腿啦、潛規則啦,巴拉巴拉的.....剝繭抽絲後,其實流程可簡化這樣

老闆不直接接觸明星,而是直接和經紀人商談,畢竟經紀人無論在經驗和精力上都有優勢。這裡經紀人其實就是代理物件,明星就是目標物件,老闆就是呼叫方。轉換為程式碼流程如下

這裡需要認真的強調一點:代理模式側重於控制訪問,代理物件不會改變目標物件的職責和能力,它提供與目標物件相同的介面,但會增加相應的邏輯來控制訪問目標物件。

有些網友提出代理模式是為了在目標物件的基礎上增強功能,如果較真的話小編並不不認同此種說法,因為增強基類的功能那是裝飾模式乾的活;代理模式和委託模式也有區別,後者是為了提供統一的介面服務,便於方便切換底層實現。

代理模式實現

代理模式的演示實現如下(為了方便觀眾觀看,我會把程式碼集中在App.java中,專案中不允許)

public class App{  public static void main( String[] args )  {    //代理物件(經紀人)    Broker broker = new Broker();    System.out.println("A老闆的唱歌演出需求");    broker.doSing();    System.out.println("---------------");    System.out.println("B老闆的跳舞演出需求");    broker.doDance();  }}/** * 經紀人 */class Broker{  /**   * 唱歌演出   */  public void doSing(){    //演出前業務處理    System.out.println("1.檢查節目是否和明星的調性匹配");    System.out.println("2.出場費是否滿足");    //演出    new Star().doSing();    //演出後業務處理    System.out.println("1.出場費尾款");    System.out.println("2.粉絲維護");  }  /**   * 跳舞演出   */  public void doDance(){    //演出前業務處理    System.out.println("1.檢查節目是否和明星的調性匹配");    System.out.println("2.出場費是否滿足");    //演出    new Star().doDance();    //演出後業務處理    System.out.println("1.出場費尾款");    System.out.println("2.粉絲維護");  }}/** * 明星 */class Star{  /**   * 唱歌技能   */  public void doSing(){    System.out.println("唱歌");  }  /**   * 跳舞技能   */  public void doDance(){    System.out.println("跳舞");  }}

老闆不直接接觸明星,而是通過經紀人滿足業務需求。細心的同學會發現,經紀人的業務處理中存在大量重複程式碼,當然你可以把演出前後的業務封裝成方法呼叫如

/** * 唱歌演出 */public void doSing(){    //演出前業務處理    beforeDo();    //演出    new Star().doSing();    //演出後業務處理    afterDo();}

但依然不美,試想如果能把經紀人的業務技能直接一一匹配到明星的技能(doSing,doDance)就方便了,於是引出了JDK動態代理。

用JDK動態代理重構

1. 為了實現“動態”需要使用面向介面的程式設計思想,把明星和經紀人抽象出一個共同的介面

/** * 明星和經紀人的介面 */interface IAct {    void doSing();    void doDance();}

2. 通過實現InvocationHandler介面來做代理業務

/** * 經紀人 */class Broker implements InvocationHandler {  //目標物件  private Object target;  public Broker(Object target){    this.target=target;  }  @Override  public Object invoke(Object proxy, Method method,         Object[] args) throws Throwable {    //演出前業務處理    System.out.println("1.檢查節目是否和明星的調性匹配");    System.out.println("2.出場費是否滿足");    //明星演出技能    Object object=method.invoke(target,args);    //演出後業務處理    System.out.println("1.出場費尾款");    System.out.println("2.粉絲維護");    return object;  }}

3. 動態建立代理物件並處理業務

public static void main( String[] args ){  //目標物件  IAct star=new Star();  //使用newProxyInstance建立動態代理物件  IAct broker=(IAct) Proxy.newProxyInstance(          IAct.class.getClassLoader(),          star.getClass().getInterfaces(),          new Broker(star)  );  //業務處理  System.out.println("A老闆的唱歌演出需求");  broker.doSing();  System.out.println("---------------");  System.out.println("B老闆的跳舞演出需求");  broker.doDance();}

經紀人執行doSing,經過業務邏輯處理後直接對映到明星的doSing,這樣就減少了很多重複的程式碼,提高了可維護性。

總結JDK實現代理模式流程如下

1. 抽象出目標物件的介面

2. 實現介面InvocationHandler建立代理業務

3. 使用newProxyInstance建立代理物件

4. 業務處理

全部程式碼如下

package com.zhaiqianfeng;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class App{  public static void main( String[] args ){    //目標物件    IAct star=new Star();    //使用newProxyInstance建立動態代理    IAct broker=(IAct) Proxy.newProxyInstance(            IAct.class.getClassLoader(),            star.getClass().getInterfaces(),            new Broker(star)    );    //業務處理    System.out.println("A老闆的唱歌演出需求");    broker.doSing();    System.out.println("---------------");    System.out.println("B老闆的跳舞演出需求");    broker.doDance();  }}/** * 明星和經紀人的介面 */interface IAct {  void doSing();  void doDance();}/** * 經紀人 */class Broker implements InvocationHandler {  //目標物件  private Object target;  public Broker(Object target){    this.target=target;  }  @Override  public Object invoke(Object proxy, Method method,           Object[] args) throws Throwable {    //演出前業務處理    System.out.println("1.檢查節目是否和明星的調性匹配");    System.out.println("2.出場費是否滿足");    //明星演出技能    Object object=method.invoke(target,args);    //演出後業務處理    System.out.println("1.出場費尾款");    System.out.println("2.粉絲維護");    return object;  }}/** * 明星 */class Star implements IAct {  /**   * 唱歌技能   */  public void doSing(){    System.out.println("唱歌");  }  /**   * 跳舞技能   */  public void doDance(){    System.out.println("跳舞");  }}
Java8 lambda表示式重構

如果只是臨時業務處理,可以使用匿名類或Java8的lambda表示式可以更優

public static void main(String[] args) {  //目標物件  IAct star = new Star();  //使用newProxyInstance建立動態代理  IAct broker = (IAct) Proxy.newProxyInstance(      IAct.class.getClassLoader(),      star.getClass().getInterfaces(),      (proxy, method, args2) -> {        //演出前業務處理        System.out.println("1.檢查節目是否和明星的調性匹配");        System.out.println("2.出場費是否滿足");        //明星演出技能        Object object = method.invoke(star, args2);        //演出後業務處理        System.out.println("1.出場費尾款");        System.out.println("2.粉絲維護");        return object;    }  );  //業務處理  System.out.println("A老闆的唱歌演出需求");  broker.doSing();  System.out.println("---------------");  System.out.println("B老闆的跳舞演出需求");  broker.doDance();}
寫在最後

JDK動態代理實際上是在執行時通過反射的方式來實現的,將代理的方法呼叫轉到到目標物件上,最終將目標物件生成的任何結果返回給呼叫方。由於這是個鏈式呼叫,所以很方便代理在目標物件方法呼叫前後增加處理邏輯。根據這種思路可以在多種設計模式中使用JDK的動態代理比如代理模式、Facade、Decorator等。

在面向方面程式設計(AOP)也應用廣泛,如事務管理、日誌記錄、資料校驗等,主要是將橫切關注點從業務邏輯中分離出來,所以一通百通。

補充一點,由於JDK的不斷優化,到JDK8的時候JDK的動態代理不比CGLIB效率低,大家可以做些實驗。

相關閱讀

最新評論
  • 整治雙十一購物亂象,國家再次出手!該跟這些套路說再見了
  • 錘子試水5G手機,是翻身之戰還是垂死掙扎?老羅:我只想當個網紅