動態代理、多型原理

sunjiaminaini發表於2017-08-08

RESTful API 呼叫很多人都在用 Retrofit,說到 Retrofit 就不得不提動態代理,雖然這不是它唯一的亮點,而且也不是動態代理的典型使用場景,但大家就是愛問:說說動態代理是怎麼回事吧?Retrofit 的解析請見 拆輪子系列:拆 Retrofit。
動態代理的原理
看過原始碼之後其實很簡單,就是一句話:執行時生成實現類(代理類)的位元組碼,對其所有的方法呼叫都轉發到 invocation handler 的 invoke 方法,在 invoke 方法中執行額外的邏輯。代理類的生成有個快取優化(比較複雜,不展開)。
為什麼說 Retrofit 對動態代理的使用並非典型場景呢?通常來說代理類(無論是手寫的靜態代理,還是動態生成的代理)都只是做些額外的工作,比例進行許可權檢查、相關初始化,實際工作還是由委託類(被代理的類)完成,但 Retrofit 實際上沒有委託類,我們只負責定義介面,所有的工作都由動態生成的代理完成。所以說並非動態代理的典型場景。
接下來再問幾個問題:
● 對介面方法的呼叫是怎麼一步步實際執行到 invoke 的?
● 虛擬函式表,執行時多型,編譯時多型?
● Java 的虛擬函式表存在哪裡的?
當然這些問題可能很多人不會問,但我還是想聊一聊。
多型的原理
多型通常可以分為兩種:編譯時多型和執行時多型。其實在 Java 裡面,還有一個類似的分類:過載(overload)和重寫(override)。可以認為編譯時多型等於過載,執行時多型等於重寫。
編譯時多型指的就是函式名相同,但是引數列表不同。例如 int add(int a, int b) 和 float add(float a, float b),在程式碼編譯的時候就可以知道呼叫的是哪個版本,怎麼確定的?引數列表呀!因為函式版本在編譯時確定,所以稱為編譯時多型。
執行時多型則是指虛擬函式的多型,父類和子類、不同子類的實現不一樣。這裡就不舉例了,Java 裡面絕大多數方法都是虛擬函式,除了建構函式、final、private、static 函式(final 比較特殊)。而在 C++ 中,只有用 virtual 關鍵字修飾的方法才是虛擬函式。由於我們可以把子類物件賦值給父類引用,所以到底應該執行父類的版本還是子類的版本,編譯期無法確定,必須在執行時檢視這個物件的實際型別,然後再呼叫這個型別的版本。這種執行時的查詢機制,就叫執行時多型。
知道一個物件的具體型別之後,怎麼確定應該執行哪個函式版本呢?我們會有一個查詢表,裡面記錄著每個函式名實際函式體的地址,知道一個物件的具體型別之後,就拿到了它的這個查詢表,所以就知道應該執行哪個函式版本了。這個查詢表就叫虛擬函式表。
那 Java 的虛擬函式表存在哪裡呢?
這是 JVM 實現細節。但我們按邏輯推斷,可以存在 class 物件那裡。一個物件包含三部分資訊,虛擬函式表、型別資訊、物件特定資訊,前兩者同一個類的所有物件都共享,所以可以放在 class 物件那裡。

文章內容來自網上資源,只為記錄相關知識,如有侵權,請聯絡作者。

相關文章