所謂動態代理,指的是語言提供的一種語法,可以將對對象中不一樣方法的調用重定向到一個統一的處理函數中來。
python重寫__getattr__
函數可以作到這一點,就連世界上最好的語言也提供稱爲魔術方法的__call
。
這種語法除了能更好的實現動態代理外,仍是RPC框架實現原理的一部分。
<!--more-->html
動態代理提供一種抽象,可以將對象中不一樣方法的調用重定向到一個統一的處理函數,作自定義的邏輯處理。
可是對於調用者,對此毫無察覺,就好像調用的方法是用傳統方式實現的通常。java
這種語法,在java中被稱爲動態代理。之因此叫作動態代理,是由於它能避免傳統代理模式實現中人工一個一個的將java函數轉發過去,
而是可以讓代碼自動作到這一點,這樣代理類的代碼是和業務無關的,不會由於業務類的方法增多而逐漸龐大。
使代碼更易維護更易修改,實現自動化搬磚。python
實際上,被代理的類不必定位於本機類,動態代理語法提供了一種抽象方式,被代理的類也能夠位於遠程主機上,這也是RPC框架實現原理的一部分。git
理解了動態代理的概念後不難發現,動態代理概念上有着這麼幾個部分:github
理解了概念,就不難理解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); } }
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)); }
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碼工還須要同時維護代理類的相關代碼,實在是累心。
經過使用動態代理,動態代理可以自動將代理類的相關方法轉發到被代理類,能夠看到:
是的,利用動態代理也能實現AOP。仔細推演一下不能得出這個結論。咱們知道:
那麼,首先使用動態代理讓代理類忠實的代理被代理類,而後處理函數中插入咱們的自定義的鉤子。
以後讓代理類替換被代理類須要使用的場景,這樣,至關於對該類的全部方法定義了一個切面。
不過,使用動態代理實現AOP特別麻煩,囉嗦。這僅僅做爲一個探討的思路,來講明動態代理這一通用概念能夠實現不少特定技術。
實際使用中固然使用spring提供的AOP更爲方便。
RPC即遠程過程調用,在分佈式的網站架構中是一個很是重要的技術,目前如今流行的SOA架構,微服務架構,它們的核心原理之一就是RPC調用。
從概念上來講,RPC的概念是很是簡潔優美的。RPC方法的調用和普通的方法並沒有二異,調用者不須要操心具體的實現,這是抽象提供的威力。
實現上,它將函數調用方和函數的提供方分散在兩個不一樣的進程上,中間使用網絡通訊來進行數據交互。
動態代理就是實現RPC的技術之一。只要理解了動態代理和RPC,咱們很容易發現這樣一個事實:
RPC調用實際上是對遠程另一臺機器進程上的對象的代理。
仔細思考RPC調用的數據流流向,就能梳理出這樣的思路:
顯而易見,第二步,須要使用動態代理將分散的函數調用轉發到一個統一的處理中心;第五步,將統一收集來的調用信息分發給具體的函數執行,顯然使用反射作到這一點。
有了這個思路,經過利用動態代理,反射,和網絡編程技術,實現一個簡易版的RPC框架也就不難了。
考慮到本文是介紹動態代理的,關於RPC的細節實現有時間新開一篇博文分析。
總得來講,經過必定的思考,我的以爲動態代理的核心在於:將分散的對對象不一樣方法的調用轉發到一個同一的處理函數中來。
有了這個關鍵點,不少其它技術的實現須要藉助於動態代理的這一個關鍵點實現,也所以動態代理也有着這麼多的應用。
注:該文於2018-03-30撰寫於個人github靜態頁博客,現同步到個人segmentfault來。