java動態代理及RPC框架介紹

所謂動態代理,指的是語言提供的一種語法,可以將對對象中不一樣方法的調用重定向到一個統一的處理函數中來。
python重寫__getattr__函數可以作到這一點,就連世界上最好的語言也提供稱爲魔術方法的__call
這種語法除了能更好的實現動態代理外,仍是RPC框架實現原理的一部分。
<!--more-->html

動態代理是什麼

動態代理提供一種抽象,可以將對象中不一樣方法的調用重定向到一個統一的處理函數,作自定義的邏輯處理。
可是對於調用者,對此毫無察覺,就好像調用的方法是用傳統方式實現的通常。java

這種語法,在java中被稱爲動態代理。之因此叫作動態代理,是由於它能避免傳統代理模式實現中人工一個一個的將java函數轉發過去,
而是可以讓代碼自動作到這一點,這樣代理類的代碼是和業務無關的,不會由於業務類的方法增多而逐漸龐大。
使代碼更易維護更易修改,實現自動化搬磚。python

實際上,被代理的類不必定位於本機類,動態代理語法提供了一種抽象方式,被代理的類也能夠位於遠程主機上,這也是RPC框架實現原理的一部分。git

理解了動態代理的概念後不難發現,動態代理概念上有着這麼幾個部分:github

  1. 給調用者使用的代理類。在java中,咱們發現動態代理提供的抽象自然契合面向接口編程,所以它也有多是接口。
  2. 一個統一的處理函數,收集不一樣函數轉發過來的請求,可自定義處理邏輯集中處理。java中它可能會成爲一個較獨立的部分,所以也多是類。

java動態代理機制

理解了概念,就不難理解java動態代理的機制了。下面來看看java動態代理機制如何代理一個本地對象。spring

代理接口

首先看第一個部分,給調用者使用的代理類。在java動態代理機制中,這個角色只能是接口。咱們定義一個整數運算接口:編程

interface NumberOperationInterface {
    int add(int a, int b);
}

代理處理器

再看第二個角色,統一的處理函數。在java中它的確是類,經過實現InvocationHandler接口定義。segmentfault

class NumberOperationImpProxyHandler implements InvocationHandler {

    private Object proxied;

    public RealObjectProxyHandler(Object proxied) {
        this.proxied = proxied;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.printf("調用函數%s\n", method.getName());
        return method.invoke(proxied, args);
    }

}
  1. 因爲咱們的例子是代理本地對象,那麼處理函數是須要被代理對象的信息。能夠看到,咱們從構造函數中將被代理對象保存在該類中,便可從處理函數中訪問到。
  2. invoke函數中,對代理對象的全部方法的調用都被轉發至該函數處理。在這裏能夠靈活的自定義各類你能想到的邏輯。在上面的代碼中,咱們使用反射調用被代理對象的同名方法實現。

被代理類

因爲咱們的示例是代理本地對象,所以還須要一個被代理對象的類:網絡

class NumberOperationImp implements NumerOperationInterface {

    @Override
    public int add(int a, int b) {
        return a + b;
    }
}

建立代理對象

好了,各個組成部分都定義完成。如今把它們組合起來:架構

public NumerOperationInterface wrap(NumerOperationInterface proxied) {
    return (NumerOperationInterface) Proxy.newProxyInstance(
        NumerOperationInterface.class.getClassLoader(),
        new Class[]{NumerOperationInterface.class},
        new NumberOperationImpProxyHandler(proxied));
}
  1. 因爲java提供的這個寫法實在是太囉嗦了,因此把它放入一個輔助函數中。
  2. Proxy.newProxyInstance 方法可以根據提供的接口和代理處理器建立代理對象。

java提供的寫法太囉嗦了,能夠考慮使用Guake提供的輔助函數簡化下代碼。以下:

public NumerOperationInterface wrap(NumerOperationInterface proxied) {
    return Reflection.newProxy(NumerOperationInterface.class, new NumberOperationImpProxyHandler(proxied));
}

好了,如今調用下試試:

NumerOperationInterface proxied = new NumberOperationImp();
real = wrap(proxied);
real.add(1, 2);

總結

動態代理聽起來是代理模式的動態實現,但是結合上面的最終效果,不以爲這個叫作動態裝飾器更合適嗎?

動態代理的應用

說完了動態代理的概念和實現機制,該看看使用動態代理有哪些應用。

應用一:代理模式/裝飾器模式的動態實現

這個應用場景前面據已經提到過。
代理模式和裝飾器模式是編程當中很經常使用的技巧,用於提高代碼的靈活性和可擴展性。
傳統代理模式的實現方式比較暴力直接,須要將全部被代理類的全部方法都寫一遍,而且一個個的手動轉發過去。
在維護被代理類的同時,做爲java碼工還須要同時維護代理類的相關代碼,實在是累心。

經過使用動態代理,動態代理可以自動將代理類的相關方法轉發到被代理類,能夠看到:

  1. 代理轉發的過程自動化了,實現自動化搬磚。
  2. 代理類的代碼邏輯和具體業務邏輯解耦,與業務無關。

應用二:實現AOP

是的,利用動態代理也能實現AOP。仔細推演一下不能得出這個結論。咱們知道:

  1. 動態代理提供了一種方式,可以將分散的方法調用轉發到一個統一的處理函數處理。
  2. AOP的實現須要可以提供這樣一種機制,即在執行函數前和執行函數後都能執行本身定義的鉤子。

那麼,首先使用動態代理讓代理類忠實的代理被代理類,而後處理函數中插入咱們的自定義的鉤子。
以後讓代理類替換被代理類須要使用的場景,這樣,至關於對該類的全部方法定義了一個切面。

不過,使用動態代理實現AOP特別麻煩,囉嗦。這僅僅做爲一個探討的思路,來講明動態代理這一通用概念能夠實現不少特定技術。
實際使用中固然使用spring提供的AOP更爲方便。

應用三:實現RPC

RPC即遠程過程調用,在分佈式的網站架構中是一個很是重要的技術,目前如今流行的SOA架構,微服務架構,它們的核心原理之一就是RPC調用。

從概念上來講,RPC的概念是很是簡潔優美的。RPC方法的調用和普通的方法並沒有二異,調用者不須要操心具體的實現,這是抽象提供的威力。
實現上,它將函數調用方和函數的提供方分散在兩個不一樣的進程上,中間使用網絡通訊來進行數據交互。

動態代理就是實現RPC的技術之一。只要理解了動態代理和RPC,咱們很容易發現這樣一個事實:
RPC調用實際上是對遠程另一臺機器進程上的對象的代理

仔細思考RPC調用的數據流流向,就能梳理出這樣的思路:

  1. 調用方調用本地的RPC代理方法,將參數提供給該方法。
  2. 不一樣的RPC代理方法被轉發到一個統一的處理中心,該處理中心知道調用的是那個函數,參數是什麼。
  3. 該處理中心將調用的信息封裝打包,經過網絡發送給另一個進程。
  4. 另一個進程接受到調用進程發送過來的數據包。
  5. 該進程根據數據包中記錄的RPC調用信息,將調用分發給對應的被代理對象的對應方法去執行。
  6. 返回的話思路相似。

顯而易見,第二步,須要使用動態代理將分散的函數調用轉發到一個統一的處理中心;第五步,將統一收集來的調用信息分發給具體的函數執行,顯然使用反射作到這一點。
有了這個思路,經過利用動態代理,反射,和網絡編程技術,實現一個簡易版的RPC框架也就不難了。
考慮到本文是介紹動態代理的,關於RPC的細節實現有時間新開一篇博文分析。

最後

總得來講,經過必定的思考,我的以爲動態代理的核心在於:將分散的對對象不一樣方法的調用轉發到一個同一的處理函數中來。

有了這個關鍵點,不少其它技術的實現須要藉助於動態代理的這一個關鍵點實現,也所以動態代理也有着這麼多的應用。

注:該文於2018-03-30撰寫於個人github靜態頁博客,現同步到個人segmentfault來。

相關文章
相關標籤/搜索