Java 基礎(十九)代理

代理模式

二十三種經典的設計模式之一,屬於一種結構模式。java

二十三種設計模式不會單獨開篇去講,有時間會彙總寫一篇文章去接單介紹二十三種模式,而後根據「建立模式」、「行爲模式」、「結構模式」三種類型去對比二十三中設計的優缺點,喜歡個人文章的小夥伴點個關注唄~程序員

定義:爲其餘對象提供一種代理以控制對這個對象的訪問。在某些狀況下,一個對象不適合或者不能直接引用另外一個對象,而代理對象能夠在客戶端和目標對象之間起到中介左右。編程

優勢

  • 職責清晰,真實的角色就是實現實際的業務邏輯,不用關係其餘非本職責的食物,經過後期的代理完成一件事物,附帶的結果就是編程簡潔清晰。
  • 代理對象能夠在客戶端和目標之間起到中介的做用,這樣起到了中介的做用和保護了目標對象的做用。
  • 高擴展性

模式結構

一個真正的你要訪問的對象(目標類),一個代理對象,真正對象與代理對象實現同一個接口,先訪問代理類再訪問真正要訪問的對象。設計模式

代理模式分爲靜態代理和動態代理。bash

靜態代理是由程序員建立或工具生成代理類的源碼,再編譯代理類。所謂靜態也就是程序運行以前就已經存在代理類的字節碼文件,代理類和委託類的關係在運行前就肯定了。框架

動態代理是在現實階段不用關心代理類,而在運行階段才指定哪個對象。ide

靜態代理

剛剛咱們說了,靜態代理在使用時,須要定義接口或者父類,被代理對象與代理對象一塊兒實現共同接口或者繼承相同父類。
其實說白了就是爲了控制對象引用,生活場景中,咱們以買車爲例,若是咱們要買一輛車必須經過汽車4S 店,汽車4S 店就是充當代理角色,其目的是物流控制買車客戶的買車行爲,必須經過汽車4S 店才能從騎車廠商買一輛車。工具

1.首先買車是一種行爲,客戶和代理廠商都須要實現的接口性能

public interface IBuyCar{
    void buyCar();
}複製代碼

2.聲明一個要買車的客戶,實現買車的接口ui

public class Customer implement IBuyCar{
    public int cash;//預算買車款
    public String name;//名字

    public Customer(int cash,String name){
        this.cash = cash;
        this.name = name
    }

    @Override
    public void buyCar(){
        System.out.println(name+"買了一輛車花了"+cash);
    }

}複製代碼

3.聲明一個買車代理騎車4S 店,一樣實現買車接口,必須接受客戶下單。

public class PorscheProxy implement IBuyCar{
    public Customer customer;//

    public PorscheProxy(Customer customer){
        this.customer = customer;
    }

    @Override
    public void buyCar(){
        customer.buyCar();
    }

}複製代碼

4.建立一個客戶端,模擬一次買車

public static void main(String[]args){
    Customer c = new Customer(10000,"ZhangSan");
    PorscheProxy proxy = new PorscheProxy(customer);
    proxy.buyCar();
}複製代碼

OK,成功的使用代理模式完成了一次購車操做。

有什麼用?

而後,可能有同窗會問了,你這個代碼優在那裏?難道客戶就不能本身去買車麼?固然能夠,若是在使用場景中實現類能知足要求時,咱們固然能夠直接實現類,但當實現類不能知足要求,要擴展需求,根據開閉原則,你又不能修改實現類代碼,這時你就用代理類。好比買車的時候,須要對客戶進行一個購車款審覈,若是符合條件就買,不符合就不能買。

@Override
public void buyCar() {//實現爲客戶買車
    if(customer.cash < 1000000){
       System.out.println("buyCar","你的錢不夠買一輛Porsche,建議去看看大衆");
       return;
    }
    customer.buyCar();
}複製代碼

優缺點

  • 優勢:能夠作到在不修改目標對象的功能的前提下,對目標功能擴展
  • 缺點:由於代理對象須要與目標對象實現同樣的接口,因此會有不少代理類,類太多。同時,一旦接口增長方法,目標對象與代理對象都要維護。

如何解決靜態代理中的缺點呢?答案是動態代理。

動態代理

剛剛咱們講的都是靜態代理,所謂的靜態代理,就是本身要爲要代理的類寫一個代理類,或者用工具爲其生成的代理類,總之,就是程序運行前就已經存在的編譯好的代理類,這樣有時會以爲很是麻煩,也致使很是不靈活,相比靜態代理,動態帶來具備更強的靈活性,由於它不用咱們在設計實現的時候就指定某一個代理類來代理哪個被代理對象,咱們能夠把這種指定延時到程序運行時由JVM 來實現。

仍是剛剛那個買車的例子

public class Client {
    public static void main(String[]args){
    IBuyCar customer = (IBuyCar)Proxy.newProxyInstance(IBuyCar.class.getClassLoader(), IBuyCar.class.getInterfaces(), new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //在轉調具體目標對象以前,能夠執行一些功能處理

            //轉調具體目標對象的方法
            Object invoke = method.invoke(this, args);

            //在轉調具體目標對象以後,能夠執行一些功能處理
            return invoke;
        }
    });
    customer.buyCar();
    }
}複製代碼

以上,就是動態代理的寫法
可能有同窗會這樣寫好像不太符合咱們的需求,我怎麼根據客戶的現金數去判斷可否買車呢?
我是這樣理解的,由於動態代理是爲了解決靈活性,因此通常不推薦強綁定業務對象,這裏咱們通常用於解決某些具備共性的場景,咱們能夠在代理方法調用先後分別幹一些想幹的事情。若是必定要強綁定業務,那麼推薦使用靜態代理哦。
固然,可能會有同窗要鑽牛角尖,我就要用動態代理實現買車的需求,且控制金額。這裏我仍是寫一下代碼實現吧,我不推薦這種作法哦,具體看業務邏輯吧~

直接貼代碼吧,建立了DynamicProxy 類繼承了InvocationHandler,而後在invoke 方法裏面判斷就好。

優缺點

  • 優勢
    • 減小編程的工做量
    • 系統擴展性和可維護性加強
  • 缺點
    • 使用了反射,下降性能

擴展

再來兩個小的擴展知識點吧~

CGLIB 代理

上面的靜態代理和動態代理模式都是要求目標實現一個接口的目標對象,可是有時候目標對象只是一個單獨的對象,並無實現任何接,這個時候就可使用以目標對象子類的方式實現代理,這種方法叫作 cglib 代理。

cglib 代理又稱子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展。

CGLIB(Code Generation Library)是一個開源項目!

  • JDK 的動態代理有一個限制,就是使用動態代理的對象必須實現一個或多個接口,若是想代理沒有實現接口的類,就可使用CGLIB 實現。
  • CGLIB 是一個強大的高性能的代碼生成包,它能夠在運行期擴展 Java 類與實現 java 接口。它普遍的被許多 AOP的框架使用,例如 Spring。
  • CGLIB 包的底層是經過使用一個小而快的字節碼處理框架 ASM 來轉換字節碼生成新的類。不鼓勵直接使用 ASM,由於它要求你必須對 JVM 內部結構包括 class 文件的格式和指令集都很熟悉。

好了,沒看懂 CGLIB 的實現原理不要緊。咱們只是簡單瞭解一下,下面仍是以買車的例子來說解。

首先,運行時經過 ASM 來生成一個代理類 CGLIBProxy,繼承子 Customer,而後重寫對應的方法,看代碼。

public class CGLIBProxy extends Customer{

    @Override
    public void buyCar(){
    //在轉調具體目標對象以前,能夠執行一些功能處理

    //轉調具體目標對象的方法
    super.buyCar();  

    //在轉調具體目標對象以後,能夠執行一些功能處理

    }

}複製代碼

以上,大概就是醬紫吧。在 Android 開發中好像沒用到這玩意,咱們知道這回事就好了哈。

Retrofit 中的代理模式

之因此單獨寫代理模式,主要是由於他喵的二十三種設計模式,就代理沒看懂,以致於我每次在研究 Retrofit 源碼的時候,看到代理全程懵逼。

很少說了,直接看 Retrofit.create()方法

public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
  eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
    new InvocationHandler() {
      private final Platform platform = Platform.get();

      @Override public Object invoke(Object proxy, Method method, Object... args)
          throws Throwable {
        // If the method is a method from Object then defer to normal invocation.
        if (method.getDeclaringClass() == Object.class) {
          return method.invoke(this, args);
        }
        if (platform.isDefaultMethod(method)) {
          return platform.invokeDefaultMethod(method, service, proxy, args);
        }
        //加載處理API接口方法
        ServiceMethod serviceMethod = loadServiceMethod(method);
        //建立OkHttpCall
        OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
        //經過對應的CallAdapter適配自定義並指望返回的Call
        return serviceMethod.callAdapter.adapt(okHttpCall);
      }
    });複製代碼

}

這裏咱們在調用Service 方法的時候,根據方法參數,建立了ServiceMethod。至於ServiceMethod是幹嗎的,這裏不做詳細講解了。

/** Adapts an invocation of an interface method into an HTTP call. */
final class ServiceMethod<T> 複製代碼

翻譯:將接口方法的調用改編爲HTTP調用

相關文章
相關標籤/搜索