當Kotlin邂逅設計模式之代理模式(二)

簡述: 從這篇文章起,咱們將繼續Kotlin邂逅設計模式系列篇中的第二篇代理模式。代理模式能夠說不少初級中級開發者迷惑的設計模式。可是它確實應用很廣,不用多說你們很是熟悉的Retrofit框架,內部使用了動態代理設計模式,以註解的方式簡化網絡請求參數傳遞,從而實現更高解耦。然而在Kotlin中有自然支持的屬性代理語法特性,能夠簡化Java中代理模式實現的模板代理。java

1、介紹

代理模式(Proxy Pattern),又稱委託模式,顧名思義就是一個對象的實現委託給另外一個代理對象來實現供外部調用。算法

2、定義

爲其餘對象提供一種代理方式來控制對某個對象的訪問,從而更好地保證了該對象對外使用的透明性。設計模式

3、基本要求

  • 一、委託對象(或者被代理對象)與代理對象須要實現相同的接口。
  • 二、代理對象中保有實際的委託對象引用,外部調用的操做或行爲都是代理對象在內部交於實際的委託對象去實現。
  • 三、爲了內部隱藏性,外部調用者直接和二者共同的接口通訊。

3、使用場景

當沒法或不想直接訪問某個對象或訪問某個對象存在困難時能夠經過一個代理對象來間接訪問。代理能夠實現方法加強,好比經常使用的日誌,緩存等;也能夠實現方法攔截,經過代理方法修改原方法的參數和返回值數組

4、UML類圖

代理模式在生活中很是常見,因爲最近身邊同事都在討論買房,這裏就以買房中介爲例來介紹咱們今天的代理模式。首先咱們須要使用UML類圖直觀地表示出代理模式思想。緩存

由上面的UML的類圖可知,主要涉及到四種角色:bash

  • 一、Client: 客戶類,能夠看作代理模式調用的外部者
  • 二、IPurchaseHouse: 抽象買房接口,該接口主要職責是聲明HouseOwner(實際房子擁有者)與HouseAgent(房產中介)的共同接口方法,該類能夠是一個接口或抽象類
  • 三、HouseOwner: 房子擁有者(房東),也就是代理模式中實際委託對象或被代理對象,外部調用者Client類就是經過代理對象(中介)間接調用實際的委託對象中定義的方法
  • 四、HouseAgent: 房產中介,也就是代理模式中的代理對象,該類持有一個真實HouseOwner引用,在代理類中接口方法中調用HouseOwner方法以此來達到代理做用。

5、靜態代理

一、Java實現靜態代理

在Java中實現靜態代理仍是比較簡單,只要按照上述UML中分析角色規則來定義就能輕鬆實現。這裏就用Java先去實現上述例子:網絡

//IPurchaseHouse: 抽象買房接口
interface IPurchaseHouse {
    void inquiryPrice();//詢價

    void visitHouse();//看房

    void payDeposit();//付定金

    void signAgreement();//籤合同

    void payMoney();//付錢

    void getHouse();//拿房
}

//HouseOwner: 房子擁有者(房東)
class HouseOwner implements IPurchaseHouse {//實現IPurchaseHouse共同接口
    @Override
    public void inquiryPrice() {
        System.out.println("HouseOwner提出房子價格: 200W RMB");
    }

    @Override
    public void visitHouse() {
        System.out.println("HouseOwner贊成買房者來看房子");
    }

    @Override
    public void payDeposit() {
        System.out.println("HouseOwner收了買房者1W RMB定金");
    }

    @Override
    public void signAgreement() {
        System.out.println("HouseOwner與買房者簽定合同");
    }

    @Override
    public void payMoney() {
        System.out.println("買房者付錢給HouseOwner");
    }

    @Override
    public void getHouse() {
        System.out.println("買房者拿到房子");
    }
}

//HouseAgent: 房產中介
class HouseAgent implements IPurchaseHouse {
    private IPurchaseHouse mHouseOwner;//具體房東HouseOwner被代理對象引用

    public HouseAgent(IPurchaseHouse houseOwner) {
        mHouseOwner = houseOwner;
    }

    @Override
    public void inquiryPrice() {
        mHouseOwner.inquiryPrice();//經過具體房東HouseOwner引用去調用inquiryPrice
    }

    @Override
    public void visitHouse() {
        mHouseOwner.visitHouse();//經過具體房東HouseOwner引用去調用visitHouse
    }

    @Override
    public void payDeposit() {
        mHouseOwner.payDeposit();//經過具體房東HouseOwner引用去調用payDeposit
    }

    @Override
    public void signAgreement() {
        mHouseOwner.signAgreement();//經過具體房東HouseOwner引用去調用signAgreement
    }

    @Override
    public void payMoney() {
        mHouseOwner.payMoney();//經過具體房東HouseOwner引用去調用payMoney
    }

    @Override
    public void getHouse() {
        mHouseOwner.getHouse();//經過具體房東HouseOwner引用去調用getHouse
    }
}

//Client客戶類
class Client {
    public static void main(String[] args) {
        IPurchaseHouse houseOwner = new HouseOwner();
        IPurchaseHouse houseAgent = new HouseAgent(houseOwner);//傳入具體被代理類實例
        houseAgent.inquiryPrice();//詢問價格
        houseAgent.visitHouse();//看房
        houseAgent.payDeposit();//支付定金
        houseAgent.signAgreement();//籤合同
        houseAgent.payMoney();//付錢
        houseAgent.getHouse();//拿房
    }
}
複製代碼

運行結果:數據結構

HouseOwner提出房子價格: 200W RMB
HouseOwner贊成買房者來看房子
HouseOwner收了買房者1W RMB定金
HouseOwner與買房者簽定合同
買房者付錢給HouseOwner
買房者拿到房子

Process finished with exit code 0
複製代碼

這就是靜態代理具體的實現,可能有些並不能看到代理模式所帶來的好處,看上去就像是代理類作了實際轉發調用而已。實際上有個很明顯優勢就是: 能夠在HouseAgent類中整個流程插入一些特有的操做或行爲,而不會影響內部HouseOwner的實現,保護內部的實現。 還有一個優勢就是代理類在保證HouseOwner核心功能同時能夠擴展其餘行爲app

上述結論可能有點抽象,假如如今有個不同需求好比A房產中介,在看房以前首先得簽定一個看房協議,可是這個協議只涉及購買用戶與中介之間的協議。因此基於代理模式很輕鬆就實現。框架

//修改後的HouseAgentA
class HouseAgentA implements IPurchaseHouse {
    private IPurchaseHouse mHouseOwner;//具體房東HouseOwner被代理對象引用
    private boolean mIsSigned;

    public HouseAgentA(IPurchaseHouse houseOwner) {
        mHouseOwner = houseOwner;
    }

    @Override
    public void inquiryPrice() {
        mHouseOwner.inquiryPrice();//經過具體房東HouseOwner引用去調用inquiryPrice
    }

    @Override
    public void visitHouse() {
        if (mIsSigned) {
            System.out.println("您已經簽定了看房協議,能夠看房了");
            mHouseOwner.visitHouse();//經過具體房東HouseOwner引用去調用visitHouse
        } else {
            System.out.println("很抱歉,您還沒簽定了看房協議,暫時不能看房");
        }
    }

    public void signVisitHouseAgreement(boolean isSigned) {
        mIsSigned = isSigned;
    }

    @Override
    public void payDeposit() {
        mHouseOwner.payDeposit();//經過具體房東HouseOwner引用去調用payDeposit
    }

    @Override
    public void signAgreement() {
        mHouseOwner.signAgreement();//經過具體房東HouseOwner引用去調用signAgreement
    }

    @Override
    public void payMoney() {
        mHouseOwner.payMoney();//經過具體房東HouseOwner引用去調用payMoney
    }

    @Override
    public void getHouse() {
        mHouseOwner.getHouse();//經過具體房東HouseOwner引用去調用getHouse
    }
}
//Client客戶類
class Client {
    public static void main(String[] args) {
        IPurchaseHouse houseOwner = new HouseOwner();
        IPurchaseHouse houseAgent = new HouseAgentA(houseOwner);//傳入具體被代理類實例
        houseAgent.inquiryPrice();//詢問價格
        ((HouseAgentA) houseAgent).signVisitHouseAgreement(true);//簽定看房合同
        houseAgent.visitHouse();//看房
        houseAgent.payDeposit();//支付定金
        houseAgent.signAgreement();//籤合同
        houseAgent.payMoney();//付錢
        houseAgent.getHouse();//拿房
    }
}
複製代碼

運行結果:

HouseOwner提出房子價格: 200W RMB
您已經簽定了看房協議,能夠看房了
HouseOwner贊成買房者來看房子
HouseOwner收了買房者1W RMB定金
HouseOwner與買房者簽定合同
買房者付錢給HouseOwner
買房者拿到房子

Process finished with exit code 0
複製代碼

二、Kotlin實現靜態代理

看到了Java中的HouseAgent和HouseAgent中代理類中實現轉發委託是否是有點無腦啊,有點機械,就像是在寫Java中的setter和getter方法同樣,太多的樣板代碼。這時候把它叫給Kotlin吧,它會讓你的代理類看起來更加簡潔和優雅,由於在Kotlin中實現代理模式有着自然優點,熟悉Kotlin的小夥伴們都知道,在Kotlin中有代理獨有語法特性,經過它就能輕鬆實現代理模式。

//IPurchaseHouseKt: 抽象買房接口
interface IPurchaseHouseKt {
    fun inquiryPrice() //詢價

    fun visitHouse() //看房

    fun payDeposit() //付定金

    fun signAgreement() //籤合同

    fun payMoney() //付錢

    fun getHouse() //拿房
}
//HouseOwnerKt: 房子擁有者(房東)
class HouseOwnerKt : IPurchaseHouseKt {
    override fun inquiryPrice() {
        println("HouseOwner提出房子價格: 200W RMB")
    }

    override fun visitHouse() {
        println("HouseOwner贊成買房者來看房子")
    }

    override fun payDeposit() {
        println("HouseOwner收了買房者1W RMB定金")
    }

    override fun signAgreement() {
        println("HouseOwner與買房者簽定合同")
    }

    override fun payMoney() {
        println("買房者付錢給HouseOwner")
    }

    override fun getHouse() {
        println("買房者拿到房子")
    }
}
//HouseAgentKt: 房產中介. 注意了,重點來了,Kotlin只須要簡單一行就替代了Java代理類全部樣板代碼
class HouseAgentKt(houseOwnerKt: IPurchaseHouseKt) : IPurchaseHouseKt by houseOwnerKt//經過by關鍵字實現代理,省略大量的代理類中的樣板代碼,這一點須要get
//Client調用處
fun main(args: Array<String>) {
    val houseOwnerKt = HouseOwnerKt()
    HouseAgentKt(houseOwnerKt).run {
        inquiryPrice()//詢問價格
        visitHouse()//看房
        payDeposit()//支付定金
        signAgreement()//籤合同
        payMoney()//付錢
        getHouse()//拿房
    }
}
複製代碼

運行結果:

HouseOwner提出房子價格: 200W RMB
HouseOwner贊成買房者來看房子
HouseOwner收了買房者1W RMB定金
HouseOwner與買房者簽定合同
買房者付錢給HouseOwner
買房者拿到房子

Process finished with exit code 0
複製代碼

可能有的小夥伴就會問了,你使用by關鍵字一下把全部的方法都給代理了,但是須要像上面新加的需求同樣,須要在某個方法調用時插入一段邏輯。這個也很是方便,只須要重寫須要改變的那個方法便可。一塊兒來瞅瞅:

//修改後的HouseAgentAKt
class HouseAgentAKt(houseOwnerAKt: IPurchaseHouseKt) : IPurchaseHouseKt by houseOwnerAKt {
    private val mHouseOwnerAKt = houseOwnerAKt
    var mIsSigned: Boolean = false
    override fun visitHouse() {//只須要重寫visitHouse便可
        if (mIsSigned) {
            println("您已經簽定了看房協議,能夠看房了")
            mHouseOwnerAKt.visitHouse()
        } else {
            println("很抱歉,您還沒簽定了看房協議,暫時不能看房")
        }
    }
}
//Client調用處
fun main(args: Array<String>) {
    val houseOwnerKt = HouseOwnerKt()
    HouseAgentAKt(houseOwnerKt).run {
        mIsSigned = true
        inquiryPrice()
        visitHouse()
        payDeposit()
        signAgreement()
        payMoney()
        getHouse()
    }
}
複製代碼

運行結果:

HouseOwner提出房子價格: 200W RMB
您已經簽定了看房協議,能夠看房了
HouseOwner贊成買房者來看房子
HouseOwner收了買房者1W RMB定金
HouseOwner與買房者簽定合同
買房者付錢給HouseOwner
買房者拿到房子

Process finished with exit code 0
複製代碼

三、揭開Kotlin中使用by代理語法糖衣

可能就會有小夥伴問了,在Kotlin中一個by關鍵字底層到底作了什麼,爲何能減小代理類中樣板代碼。

實際上,在Kotlin中代理類HouseAgentKt的超類型IPurchaseHouseKt後面的by houseOwnerKt 表示houseOwnerKt將會在HouseAgentKt中內部存儲,而且編譯器將自動生成委託給houseOwnerKt的全部IPurchaseHouseKt接口方法

咱們能夠一塊兒來看下反編譯後的代碼,驗證咱們的結論:

public final class HouseAgentKt implements IPurchaseHouseKt {
   // $FF: synthetic field
   private final IPurchaseHouseKt $$delegate_0;//houseOwnerKt的內部存儲$$delegate_0

   public HouseAgentKt(@NotNull IPurchaseHouseKt houseOwnerKt) {
      Intrinsics.checkParameterIsNotNull(houseOwnerKt, "houseOwnerKt");
      super();
      this.$$delegate_0 = houseOwnerKt;
   }

   public void getHouse() {
      this.$$delegate_0.getHouse();//委託給$$delegate_0(也便是傳入的houseOwnerKt)getHouse方法
   }

   public void inquiryPrice() {
      this.$$delegate_0.inquiryPrice();//委託給$$delegate_0(也便是傳入的houseOwnerKt)inquiryPrice方法
   }

   public void payDeposit() {
      this.$$delegate_0.payDeposit();//委託給$$delegate_0(也便是傳入的houseOwnerKt)payDeposit方法
   }

   public void payMoney() {
      this.$$delegate_0.payMoney();//委託給$$delegate_0(也便是傳入的houseOwnerKt)payMoney方法
   }

   public void signAgreement() {
      this.$$delegate_0.signAgreement();//委託給$$delegate_0(也便是傳入的houseOwnerKt)signAgreement方法
   }

   public void visitHouse() {
      this.$$delegate_0.visitHouse();//委託給$$delegate_0(也便是傳入的houseOwnerKt)visitHouse方法
   }
}
複製代碼

6、動態代理

如今咱們需求又增長了,如今須要增長多個代理中介,可能有不少小夥伴就說再去按照規則增長几個代理類就能夠了。儘管Kotlin能解決Java中須要編寫不少樣板代碼的問題,可是始終仍是靜態的。所謂靜態就是代理者類是須要開發者本身手動編寫,在代碼運行前代理類的class編譯文件就已經存在。甚至,可能不是編譯前就能決定的代理類的個數,而是在運行時肯定增長代理中介的個數,面對這樣場景,靜態代理可能就無能爲力,那麼就引出下面的動態代理

一、Java實現動態代理

在Java中給咱們提供了一個很是方便的動態代理接口InvocationHandler,只要實現這個接口而後重寫它的抽象方法invoke()

//DynamicProxy類
class DynamicProxy implements InvocationHandler {
    private Object object;//被代理類的引用

    DynamicProxy(Object object) {//傳入被代理類的實例引用
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(object, args);
    }
}
//Client類
class Client {
    public static void main(String[] args) {
        IPurchaseHouse houseOwner = new HouseOwner();
        DynamicProxy dynamicProxy = new DynamicProxy(houseOwner);
        //Proxy.newProxyInstance方法動態構造一個代理中介,須要傳入被代理類的ClassLoader、共同接口集合和dynamicProxy實例對象
        IPurchaseHouse agentA = (IPurchaseHouse) Proxy.newProxyInstance(houseOwner.getClass().getClassLoader(), new Class[]{IPurchaseHouse.class}, dynamicProxy);
        agentA.inquiryPrice();
        agentA.visitHouse();
        agentA.payDeposit();
        agentA.signAgreement();
        agentA.payMoney();
        agentA.getHouse();
    }
}
複製代碼

運行結果:

HouseOwner提出房子價格: 200W RMB
HouseOwner贊成買房者來看房子
HouseOwner收了買房者1W RMB定金
HouseOwner與買房者簽定合同
買房者付錢給HouseOwner
買房者拿到房子

Process finished with exit code 0
複製代碼

二、Kotlin實現動態代理

實際上Java中的動態代理實現已經很是精簡了,因此在Kotlin在動態代理實現並無特別不同的,它和Java的實現沒有不一樣。因此這裏就再也不重複實現,只是換了Kotlin語言實現沒有什麼不同的。

7、動態代理原理解析

一、原理結論闡述

動態代理與靜態代理不一樣點在於,它能夠動態生成任意個代理對象,無須要開發者手動編寫代理類代碼。動態代理機制在運行時動態生成代理類字節碼byte數組,而後經過jvm內部將字節碼byte數組反序列化對應代理的Class對象,而後再經過反射機制建立代理類的實例

二、源碼分析論證

  • 一、第一步咱們先從Proxy.newProxyInstance方法進入探究,經過它在外部更爲直觀是能夠獲取代理類對象。
class Client {
    public static void main(String[] args) {
        IPurchaseHouse houseOwner = new HouseOwner();
        DynamicProxy dynamicProxy = new DynamicProxy(houseOwner);
        //第一步: 從Proxy.newProxyInstance方法入手
        IPurchaseHouse agentA = (IPurchaseHouse) Proxy.newProxyInstance(
                houseOwner.getClass().getClassLoader(),
                new Class[]{IPurchaseHouse.class},
                dynamicProxy
        );
        agentA.inquiryPrice();
        agentA.visitHouse();
        agentA.payDeposit();
        agentA.signAgreement();
        agentA.payMoney();
        agentA.getHouse();
    }
}
複製代碼
  • 二、第二步進入Proxy.newProxyInstance方法的定義

Proxy.newProxyInstance有三個參數:

loader(ClassLoader): 這個參數是實際被代理類的類加載器實例。

interfaces(Class<?>[]): 代理類和被代理類共同實現的接口的Class數組

h(InvocationHandler): 代理攔截器接口,通常須要使用子類去實現該接口或匿名類去實現

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
        Objects.requireNonNull(h);
       
        final Class<?>[] intfs = interfaces.clone();//將interfaces的Class數組clone一份副本,賦值給intfs
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {//檢查建立一個新的代理類須要權限
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /* * Look up or generate the designated proxy class. */
         //注意點1: getProxyClass0方法拿到代理類的Class對象實例cl
         //注意傳入的參數就是從外部傳入的loader(被代理類的類加載器)、intfs(被代理類實現所接口的Class[]的副本)
        Class<?> cl = getProxyClass0(loader, intfs);

        /* * Invoke its constructor with the designated invocation handler. */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            
            //注意點2: 拿到cl實例後,就經過反射機制建立代理類實例
            final Constructor<?> cons = cl.getConstructor(constructorParams);//先拿到代理類的構造器Constructor實例cons
            final InvocationHandler ih = h;
            //檢查代理類構造器是不是公有的public權限, 不是就會經過AccessController去修改訪問權限以至於能夠建立代理類實例
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);//將訪問權限設置爲可訪問的
                        return null;
                    }
                });
            }
            //注意點3: 拿到構造器實例cons後,就到了最爲關鍵的也就是最後一步,建立代理類實例。
            //可是須要注意的是構造器反射傳入的參數是h,也就是傳入的InvocationHandler的實例,也能夠進一步推論生成的代理類中存在以InvocationHandler爲參數的構造器。
            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);
        }
    }
複製代碼

再一次來梳理下newProxyInstance源碼流程:

首先傳入loaderinterfacesh三個參數,先將interfacesclone一份副本保存在intfs中,而後檢查建立一個新的代理類所須要的權限,接着到了咱們第一個注意點1,就是經過getProxyClass0方法(須要傳入loaderintfs參數)得到代理類的Class對象實例。拿到了代理類實例後,咱們就經過反射的機制建立代理類實例

到了咱們的注意點二,經過代理類Class對象cl得到構造器對象cons,並檢查構造器對象是不是public,不然就強行修改訪問權限

最後到了注意點三,經過cons.newInstance建立代理類對象,而且構造器反射中傳入h(InvocationHandler對象),說明咱們能夠推斷一下生成的代理類中存在以InvocationHandler爲參數的構造器

  • 三、第三步進入getProxyClass0方法,傳入的參數loaderintfs,在該方法內部會委託給proxyClassCache的get方法,若是給定的類加載器中定義的代理類實現了給定的接口,直接返回緩存中的副本,不然它將經過ProxyClassFactory建立代理類.
private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        //請注意上面那段英文註釋: 若是給定的類加載器中定義的代理類實現了給定的接口,
        //那麼就會直接返回緩存中的副本,不然它將經過ProxyClassFactory建立代理類
        //注意點1: proxyClassCache;注意點2: ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }
複製代碼
  • 四、第四步proxyClassCache的介紹和定義, 請注意建立proxyClassCache傳入的構造器兩個參數分別是: KeyFactoryProxyClassFactory
/** * a cache of proxy classes */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
複製代碼

proxyClassCache是一個WeakCache<K,P,V>對象,WeakCache<K,P,V>中的K表示key值,P表明參數,V表明存儲的值。此類用於緩存(key, sub-key) -> value鍵值對。內部具體實現是藉助了ConcurentMap<Object, ConcurrentMap<Object, Supplier<V>>>,Supplier是一個接口,就一個get方法用於得到值,不過是泛型V的包裝類,第一個Object就是key(這裏表達式不用泛型K是由於key值能夠爲null),第二個就是sub-key,那麼它對應着什麼呢? 並且具體的緩存中也沒有泛型P呢,這就須要引出另一個函數接口BiFunction<T, U, R>,該接口內部存在R apply(T t, U u)方法,這個方法意思就是根據傳入兩個泛型TU的值通過必定計算獲得泛型R的值。在WeakCache<K,P,V>類中存在兩個BiFunction對象:

final class WeakCache<K, P, V> {
    private final ReferenceQueue<K> refQueue
        = new ReferenceQueue<>();
    // the key type is Object for supporting null key
    private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
        = new ConcurrentHashMap<>();
    private final ConcurrentMap<Supplier<V>, Boolean> reverseMap
        = new ConcurrentHashMap<>();
    private final BiFunction<K, P, ?> subKeyFactory;
    private final BiFunction<K, P, V> valueFactory;
    
    public WeakCache(BiFunction<K, P, ?> subKeyFactory, BiFunction<K, P, V> valueFactory) {
        //根據K,P獲得sub-key算法
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        //根據K,P獲得value算法
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }
    ...
}    
複製代碼

WeakCahe類中只有一個核心get方法,裏面包含了整個緩存的邏輯,注意咱們獲取代理類Class對象,就是經過proxyClassCache.get(loader, interfaces);實際上就是調用WeakCache中的get方法.

//K泛型是一級map的緩存key, P泛型傳入的參數,分別對應外部傳入的 loader和 interfaces
public V get(K key, P parameter) {
        ...
        //經過傳入一級map的key,經過CacheKey拿到最終
        Object cacheKey = CacheKey.valueOf(key, refQueue);
        // 懶初始化cacheKey對應的二級valuesMap, 若是valuesMap爲空,就會新建立一個空的ConcurrentMap的valueMap,put到一級緩存map中
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
        //若是valuesMap爲空,就會新建立一個空的ConcurrentMap的valueMap
            ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey,valuesMap = new ConcurrentHashMap<>());
            //若是內部已經存在原來的oldValuesMap直接用它
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

 //------注意點1: subKeyFactory.apply(key, parameter)-----
        //根據傳入的一級map的key和參數parameter,經過subKeyFactory中的apply方法得到sub-key,
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        //而後經過咱們的sub-key,從二級緩存的valuesMap中取的supplier對象
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                //supplier這個對象多是Factory或CacheValue<V>對象,
                //那麼也就是supplier.get()方法多是調用了Factory中的get方法或CacheValue<V>中的get方法
//--------注意點2: supplier.get()----- 
                V value = supplier.get();
                //若是value不爲空就返回value,結束整個get方法調用
                if (value != null) {
                    return value;
                }
            }
            //若是緩存中沒有supplier對象
            //或者supplier中get返回是null
            //或者Factory對象沒有在CacheValue中被成功建立
            
            //factory爲null,就會建立一個新的Factory實例
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            //supplier爲null
            if (supplier == null) {
                //根據新建立的factory和subKey拿到supplier對象,若是valuesMap中存在subKey, factory鍵值對,就返回已經存在的值,沒有直接返回null
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                 //若是拿到supplier爲null,supplier就變爲了factory,這就是前面說supplier爲一個factory 
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    supplier = factory;
                } else {
                    //經過valuesMap.get()拿到supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }
複製代碼

咱們來一塊兒梳理下WeakCache的邏輯: 首先proxyClassCache就是一個WeakCache實例對象,它有兩個構造器參數subKeyFactoryvalueFactory,建立proxyClassCache實例對應傳入的是proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory())中的KeyFactoryProxyClassFactory.

而後在WeakCache內部存在二級ConcurrentHashMap, 一級map的key就是get方法傳入的key, 經過這個key拿到cacheKey,從而拿到對應的valuesMap二級map

而後又經過根據傳入的一級map的key和參數parametersubKeyFactory中的apply方法得到sub-key,經過sub-key拿到二級map中存儲的Supplier對象,它多是一個CacheValue也有多是一個Factory,

最終經過Factoryget方法拿到實際的值。

對於上述有兩個核心注意點

注意點1----->獲取subKey過程: 經過subKeyFactory.apply(key,parameter)拿到sub-key

//weakCache調用處: 
 Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
 //KeyFactory的定義
    private static final class KeyFactory
        implements BiFunction<ClassLoader, Class<?>[], Object>
    {
        @Override
        public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
            //能夠看到是根據被代理類實現的接口的Class數組長度來決定選用哪種subKey
            switch (interfaces.length) {
                //對於被代理類只實現了1個接口狀況,也是最頻繁一種
                case 1: return new Key1(interfaces[0]); // the most frequent
                 //對於被代理類只實現了2個接口狀況
                case 2: return new Key2(interfaces[0], interfaces[1]);
                 //對於被代理類只實現了0個接口狀況
                case 0: return key0;
                 //對於被代理類只實現了超過2個接口狀況
                default: return new KeyX(interfaces);
            }
        }
    }
複製代碼

注意點2----> supplier.get()獲取value的過程:

咱們都知道supplier對應的能夠是Factory對象,也就是最後會調用Factory中的get方法。

@Override
        public synchronized V get() { // serialize access
            // 再一次檢查supplier,若是傳入從valuesMap拿到的不等於當前Factory對象,由於它可能已經變成CacheValue了,那就直接返回null
            Supplier<V> supplier = valuesMap.get(subKey);
            if (supplier != this) {
                return null;
            }
            
            //建立一個新的value
            V value = null;
            try {
               //注意點出現,value最終會經過valueFactory.apply(key, parameter)拿到
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            // 判斷value是否爲null
            assert value != null;

            // 將拿到的value包裝成一個CacheValue
            CacheValue<V> cacheValue = new CacheValue<>(value);

            // 嘗試把valuesMap中的對應subKey的Factory替換成cacheValue,
            //這就是爲何前面說過valuesMap中取出的Supplier多是Factory多是CacheValue,有沒有種偷樑換柱的趕腳
            if (valuesMap.replace(subKey, this, cacheValue)) {
                //替換成功後,並把cacheValue put到reverseMap中
                reverseMap.put(cacheValue, Boolean.TRUE);
            } else {
                throw new AssertionError("Should not reach here");
            }

            // 成功替換了新的CacheValue,並返回最終的值
            return value;
        }
複製代碼

經過上述代碼分析,咱們知道最終value獲取是來自於valueFactoryapply方法,還記得valueFactory是啥嗎?沒錯它就是ProxyClassFactory也就是最終定位到了ProxyClassFactory中的apply方法。這也就是爲何以前說若是緩存中有直接從緩存中返回緩存的副本,沒有就在ProxyClassFactory中建立代理對象。

  • 五、第五步進入ProxyClassFactory中的apply方法進行探究,這是建立新的代理類Class對象惟一來源。
private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // 全部生成的代理類名稱統一前綴$Proxy
        private static final String proxyClassNamePrefix = "$Proxy";
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

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

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                //驗證類加載器是否將此接口的名稱解析爲同一個Class對象
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                //驗證interfaceClass的Class對象是不是一個接口
                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;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
            //記錄非公共代理接口的包,以便proxy類將在同一個包中定義。驗證全部非公共代理接口是否在同一個包中
            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");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
            //生成惟一的代理類名稱標識
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            //生成肯定的代理類Class文件的byte數組
            //------注意點ProxyGenerator.generateProxyClass-----
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
            //經過defineClass0方法傳入被代理類的類加載器、代理類惟一名稱、生成的代理類文件反序列化成一個代理類的Class對象
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
    }
複製代碼

再從新梳理一下ProxyClassFactory中的apply中的邏輯,首先作一些接口驗證操做,而後經過ProxyGenerator.generateProxyClass生成肯定的代理類Class文件的byte數組,最後經過defineClass0方法傳入被代理類的類加載器、代理類惟一名稱、生成的代理類文件反序列化成一個代理類的Class對象

  • 六、第六步進入ProxyGenerator中的generateProxyClass方法進行探究,主要經過它來生成代理類Class文件。generateProxyClass方法傳入的參數主要有: proxyName(惟一代理類名稱), interfaces(須要代理的接口Class數組), accessFlags(訪問權限標識)
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        //-----注意點----調用ProxyGenerator中的generateClassFile方法
        final byte[] var4 = var3.generateClassFile();
        //是否須要把生成Class文件保存在本地文件中,這個標識能夠從外部進行配置
        //boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                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");
                        }

                        Files.write(var2, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }

        return var4;
    }
複製代碼
  • 七、第七步進入generateClassFile()方法,該方法主要生成Class文件。
private byte[] generateClassFile() {
        //---注意點1----在生成的代理類中加入Object類幾個默認方法好比常見的hashCode、equal、toString方法
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        //取出代理類的接口的Class數組
        Class[] var1 = this.interfaces;
        int var2 = var1.length;

        int var3;
        Class var4;
        //----注意點2---遍歷代理類的接口的Class數組,將代理類接口中的方法加入生成的代理類中
        for(var3 = 0; var3 < var2; ++var3) {
            var4 = var1[var3];
            //得到每一個接口中定義的全部方法Method對象
            Method[] var5 = var4.getMethods();
            int var6 = var5.length;
            //而後再遍歷全部的Method對象,並把它加入到生成的代理類中
            for(int var7 = 0; var7 < var6; ++var7) {
                Method var8 = var5[var7];
                this.addProxyMethod(var8, var4);
            }
        }
        ...
        try {
        //----注意點3 生成的代理類中加入生成構造器方法generateConstructor----
            this.methods.add(this.generateConstructor());
            var11 = this.proxyMethods.values().iterator();
            ...
       //----注意點4 生成的代理類中加入生成靜態初始化塊----
            this.methods.add(this.generateStaticInitializer());
        } catch (IOException var10) {
            throw new InternalError("unexpected I/O Exception", var10);
        }
         
            ...
            //建立字節數組輸出流
            ByteArrayOutputStream var13 = new ByteArrayOutputStream();
            DataOutputStream var14 = new DataOutputStream(var13);

            try {
                ...
                var14.writeShort(this.fields.size());
                var15 = this.fields.iterator();
                //往輸出流寫入生成代理類Filed字段相關信息
                while(var15.hasNext()) {
                    ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                    var20.write(var14);
                }

                var14.writeShort(this.methods.size());
                var15 = this.methods.iterator();
                //往輸出流寫入生成代理類Method方法相關信息
                while(var15.hasNext()) {
                    ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                    var21.write(var14);
                }

                var14.writeShort(0);
                //返回最終的Class文件的字節數組
                return var13.toByteArray();
            } catch (IOException var9) {
                throw new InternalError("unexpected I/O Exception", var9);
            }
        }
    }
複製代碼
  • 八、以上就是整個動態代理中代理類生成代碼的過程,爲了進一步弄明白動態的機制,好比invoke是怎麼調用的呢。不妨咱們把生成代碼保存在本地文件中,而後一塊兒來看下生成的代理類長啥樣。
public final class $Proxy1 extends Proxy implements IPurchaseHouse {
    private static Method m1;
    private static Method m7;
    private static Method m8;
    private static Method m2;
    private static Method m4;
    private static Method m3;
    private static Method m6;
    private static Method m0;
    private static Method m5;

    //----注意點1 生成代理類中的構造器中有個InvocationHandler參數----
    public $Proxy1(InvocationHandler var1) throws  {
        super(var1);//並把它傳給它的父類Proxy中的h(InvocationHandler)
    }

    //生成equals方法
    public final boolean equals(Object var1) throws {
        try {
          //---注意點出現super.h.invoke---
          //委託父類`Proxy`中的`h`中的`invoke`方法來實現調用,並把當前生成的代理類實例this、當前方法對應的`Method`對象和參數數組`args`經過`invoke`回調出去,此時`InvocationHandler`子類中的`invoke`方法就會得以觸發
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
    //生成Object類中默認的hashCode方法
    public final int hashCode() throws {
        try {
          //---注意點出現super.h.invoke---
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    //生成Object類中默認的toString方法
    public final String toString() throws {
        try {
          //---注意點出現super.h.invoke---
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    //生成代理接口中的payDeposit方法
     public final void payDeposit() throws {
        try {
            //---注意點出現super.h.invoke 同理---
            super.h.invoke(this, m7, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    //生成代理接口中的signAgreement方法
    public final void signAgreement() throws {
        try {
          //---注意點出現super.h.invoke 同理---
            super.h.invoke(this, m8, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    //生成代理接口中的payMoney方法
    public final void payMoney() throws {
        try {
          //---注意點出現super.h.invoke 同理---
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    //生成代理接口中的getHouse方法
    public final void getHouse() throws {
        try {
         //---注意點出現super.h.invoke 同理---
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    //生成代理接口中的visitHouse方法
    public final void visitHouse() throws {
        try {
         //---注意點出現super.h.invoke 同理---
            super.h.invoke(this, m6, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    //生成代理接口中的inquiryPrice方法
    public final void inquiryPrice() throws {
        try {
          //---注意點出現super.h.invoke 同理---
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
   
   //生成的靜態初始化塊中,經過反射拿到對應的方法Method對象,
   //其中包括了Object中的方法和代理接口中的方法
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m7 = Class.forName("com.mikyou.design_pattern.delegates.dynamic.IPurchaseHouse").getMethod("payDeposit");
            m8 = Class.forName("com.mikyou.design_pattern.delegates.dynamic.IPurchaseHouse").getMethod("signAgreement");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.mikyou.design_pattern.delegates.dynamic.IPurchaseHouse").getMethod("payMoney");
            m3 = Class.forName("com.mikyou.design_pattern.delegates.dynamic.IPurchaseHouse").getMethod("getHouse");
            m6 = Class.forName("com.mikyou.design_pattern.delegates.dynamic.IPurchaseHouse").getMethod("visitHouse");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m5 = Class.forName("com.mikyou.design_pattern.delegates.dynamic.IPurchaseHouse").getMethod("inquiryPrice");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
複製代碼

其實當你看到了生成的代理類的代碼後,你就會發現動態代理的機制就很是一目瞭然。你也就明白了InvocationHandler中的invoke方法何時調用了。那咱們再來總體梳理下動態代理核心機制,其實最爲核心的就是InvocationHandler:

首先,咱們須要去實現一個InvocationHandler的子類,重寫它的invoke方法,該方法中會回調三個參數: Object proxy, Method method, Object[] args,而後在咱們在invoke方法中只須要經過調用methodinvoke方法,並傳入args參數。

而後咱們去建立一個代理類實例是經過Proxy.newProxyInstance,會傳入InvocationHandler子類實例,並把這個InvocationHandler子類實例做爲生成新的代理類的構造器函數參數,並把這個參數傳給新的代理類的父類Proxy,在Proxy中會維護這個InvocationHandler子類實例h

而後經過上述生成的代理類代碼來看,會把全部方法都轉成對應的Method對象,並在靜態初始化塊中經過反射進行初始化,而後每一個方法內部調用實現,都會委託父類Proxy中的h中的invoke方法來實現調用,並把當前生成的代理類實例、當前方法對應的Method對象和參數數組args經過invoke回調出去,此時InvocationHandler子類中的invoke方法會得以觸發,那麼在其內部又轉爲method調用它的invoke方法,並傳入args參數就至關於利用反射去調用這個方法。

最後到這裏,有關動態代理內容就算說完了。

歡迎關注Kotlin開發者聯盟,這裏有最新Kotlin技術文章,每週會不按期翻譯一篇Kotlin國外技術文章。若是你也喜歡Kotlin,歡迎加入咱們~~~

Kotlin系列文章,歡迎查看:

Kotlin邂逅設計模式系列:

數據結構與算法系列:

翻譯系列:

原創系列:

Effective Kotlin翻譯系列

實戰系列:

相關文章
相關標籤/搜索