反射原理及動態代理模式

反射原理及動態代理模式

反射(Reflect)

反射用例:https://blog.csdn.net/qq_42651904/article/details/102762577

反射學習資料:

反射之中包含了一個「反」字,所以瞭解反射我們先從「正」開始。

一般情況下,我們使用某個類時必定知道它是什麼類,是用來做什麼的。於是我們直接對這個類進行實例化,之後使用這個類對象進行操作。

反射則是一開始並不知道我要初始化的類對象是什麼,自然也無法使用 new 關鍵字來創建對象了。這時候,我們使用 JDK 提供的反射 API 進行反射調用。反射就是在運行時才知道要操作的類是什麼,並且可以在運行時獲取類的完整構造,並調用對應的方法。

Reflection(反射)是Java被視爲動態語言的關鍵,反射機制允許程序在執行期藉助於Reflection API取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法。

Java反射機制主要提供了以下功能:

  • 在運行時構造任意一個類的對象
  • 在運行時獲取任意一個類所具有的成員變量和方法
  • 在運行時調用任意一個對象的方法(屬性)

Java 是一門面向對象的語言。在面向對象的世界裏,萬事萬物皆對象,既然萬事萬物皆對象,那麼我們的類是不是對象呢?我們寫的每一個類都可以看成一個對象,是 java.lang.Class 類的對象。每一個類對應的Class放在哪裏呢?當我們寫完一個類的Java文件,編譯成class文件的時候,編譯器都會將這個類的對應的class對象放在class文件的末尾。裏面都保存了些什麼?大家可以理解保存了類的元數據信息,一個類的元數據信息包括什麼?有哪些屬性,方法,構造器,實現了哪些接口等等,那麼這些信息在Java裏都有對應的類來表示。

Class類

Class是一個類,封裝了當前對象所對應的類的信息

一個類中有屬性,方法,構造器等,比如說有一個Person類,一個Order類,一個Book類,這些都是不同的類,現在需要一個類,用來描述類,這就是Class,它應該有類名,屬性,方法,構造器等。Class是用來描述類的類。

Class類是一個對象照鏡子的結果,對象可以看到自己有哪些屬性,方法,構造器,實現了哪些接口等等

對於每個類而言,JRE 都爲其保留一個不變的 Class 類型的對象。一個 Class 對象包含了特定某個類的有關信息。 

對象只能由系統建立對象,一個類(而不是一個對象)在 JVM 中只會有一個Class實例

獲取Class對象的三種方式

1.通過類名獲取      類名.class    

2.通過對象獲取      對象名.getClass()

3.通過全類名獲取    Class.forName(全類名)

Class類的常用方法

類加載器、構造器、Method、Field

參見包cn.enjoyedu.refle.more下對應的類

代理模式(靜態代理和動態代理)

靜態代理

代理模式給某一個對象提供一個代理對象,並由代理對象控制對原對象的引用。通俗的來講代理模式就是我們生活中常見的中介。

舉個例子來說明:張三想買某種用品,雖然他可以自己去找,但是這確實太浪費時間和精力了,或者不好意思去買。於是張三就通過中介Mark來買,Mark來幫張三,張三隻是負責選擇自己喜歡的的size,然後付錢就可以了。

目的:(1)通過引入代理對象的方式來間接訪問目標對象,防止直接訪問目標對象給系統帶來的不必要複雜性; (2)通過代理對象對原有的業務增強;

代理模式一般會有三個角色:

抽象角色:指代理角色和真實角色對外提供的公共方法,一般爲一個接口

真實角色:需要實現抽象角色接口,定義了真實角色所要實現的業務邏輯,以便供代理角色調用。也就是真正的業務邏輯在此。

代理角色:需要實現抽象角色接口,是真實角色的代理,通過真實角色的業務邏輯方法來實現抽象方法,並可以附加自己的操作。將統一的流程控制都放到代理角色中處理!

而訪問者不再訪問真實角色,而是去訪問代理角色。

靜態代理在使用時,需要定義接口或者父類,被代理對象與代理對象一起實現相同的接口或者是繼承相同父類。一般來說,被代理對象和代理對象是一對一的關係,當然一個代理對象對應多個被代理對象也是可以的。

靜態代理,一對一則會出現時靜態代理對象量多、代碼量大,從而導致代碼複雜,可維護性差的問題,一對多則代理對象會出現擴展能力差的問題。

動態代理

是指在使用時再創建代理類和實例

優點

只需要1個動態代理類就可以解決創建多個靜態代理的問題,避免重複、多餘代碼

更強的靈活性

缺點

效率低,相比靜態代理中 直接調用目標對象方法,動態代理則需要先通過Java反射機制 從而 間接調用目標對象方法

應用場景侷限,因爲 Java 的單繼承特性(每個代理類都繼承了 Proxy 類),即只能針對接口 創建 代理類,不能針對類創建代理類。

在java的動態代理機制中,有兩個重要的類或接口,一個是InvocationHandler接口、另一個則是 Proxy類,這個類和接口是實現我們動態代理所必須用到的。

InvocationHandler接口是給動態代理類實現的,負責處理被代理對象的操作的,而Proxy是用來創建動態代理類實例對象的,因爲只有得到了這個對象我們才能調用那些需要代理的方法。

具體使用,參見包cn.enjoyedu.proxy.dynamic下MarkCompany類

動態代理實現原理

通過調試模式我們發現,動態代理裏,代理類的類名是這樣的:

這個代理類爲何是這個名字?它是如何執行被代理對象的相關方法呢?我們在java文件編譯後的目錄裏其實找不到這個名爲$Proxy0的class文件的。

觀察Proxy.newProxyInstance方法,與創建對象有關的代碼主要有:

獲得代理類的class對象:

獲得代理類的構造器:

創建代理類的實例

看來其中的關鍵點就是如何獲得代理類的class對象,我們進入getProxyClass0方法,進而進入proxyClassCache.get方法,通過這個這個方法所在的類名,我們可以推測,JDK內部使用了某種機制緩存了我們的代理類的class對象,同時get方法接受的參數是被代理類的類加載器和類實現的的接口。

在這個get方法中,除去和緩存相關的操作,同時用到了被代理類的類加載器和類實現的的接口這兩個參數的是

我們再進入這個方法的實現,

而最終生成代理類的class對象是defineClass0方法,但是這個方法是個native方法,所以我們不去也無法深究它,但是通過這個方法的參數我們可以明顯看到它接收了上面所生成的byte數組。

而我們通過ProxyUtils,這個自己寫的工具類,將這個byte數組寫入文件,我們並反編譯,我們將會看到

同時我們還會看到其中實現了業務接口的方法

而h則來自派生類Proxy中

這個h的實例來自哪裏?不就是我們在創建代理類的實例時傳入的嗎?

使用了動態代理的`Retrofit

Retrofit簡單的說就是一個網絡請求的適配器,它將一個基本的Java接口通過動態代理的方式翻譯成一個HTTP請求,並通過OkHttp去發送請求。此外它還具有強大的可擴展性,支持各種格式轉換以及RxJava。我們基於Retrofit2解析。

先定義一個名爲X的java接口,當然裏面有各種註解。

@FormUrlEncoded註解表示from表單,另外還有@Multipart等註解。@POST表示post請求,此外還可以使用@GET請求

然後如何使用的呢?

首先將域名傳入構造一個Retrofit,然後通過retrofit中的create方法傳入一個Java接口並得到一個x(當然x這個對象是經過處理了的)調用getPersonalListInfo(12)然後返回一個Call,最後這個Call調用了enqueue方法去異步請求http,這就是一個基本的Retrofit的網絡請求。Retrofit2中Call接口的默認實現是OkHttpCall,它默認使用OkHttp3作爲底層http請求client。

我們只定義了一個接口X,並沒有實現這個接口,那麼它是如何工作的呢?我們看看create方法的實現。

create()方法是個泛型方法,調用它時會返回一個範型T的對象,我們這裏類型是X接口,在內部實現上,很明顯了使用了動態代理返回了一個X的代理類。當調用X內部方法的時候,會調用invoke方法。invoke方法內則通過內部一系列的封裝最後返回一個Call對象。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章