天天看點

靜态代理和動态代理

代理是英文 Proxy 翻譯過來的。我們在生活中見到過的代理,大概最常見的就是朋友圈中賣面膜的同學了。

她們從廠家拿貨,然後在朋友圈中宣傳,然後賣給熟人。

靜态代理和動态代理

按理說,顧客可以直接從廠家購買産品,但是現實生活中,很少有這樣的銷售模式。一般都是廠家委托給代理商進行銷售,顧客跟代理商打交道,而不直接與産品實際生産者進行關聯。

是以,代理就有一種中間人的味道。

接下來,我們說說軟體中的代理模式。

代理模式是面向對象程式設計中比較常見的設計模式。

靜态代理和動态代理

這是常見代理模式常見的 UML 示意圖。

需要注意的有下面幾點:

1. 使用者隻關心接口功能,而不在乎誰提供了功能。上圖中接口是 Subject。

2. 接口真正實作者是上圖的 RealSubject,但是它不與使用者直接接觸,而是通過代理。

3. 代理就是上圖中的 Proxy,由于它實作了 Subject 接口,是以它能夠直接與使用者接觸。

4. 使用者調用 Proxy 的時候,Proxy 内部調用了 RealSubject。是以,Proxy 是中介者,它可以增強 RealSubject 操作。

如果難于了解的話,我用事例說明好了。值得注意的是,代理可以分為靜态代理和動态代理兩種。先從靜态代理講起。

我們平常去電影院看電影的時候,在電影開始的階段是不是經常會放廣告呢?

電影是電影公司委托給影院進行播放的,但是影院可以在播放電影的時候,産生一些自己的經濟收益,比如賣爆米花、可樂等,然後在影片開始結束時播放一些廣告。

現在用代碼來進行模拟。

首先得有一個接口,通用的接口是代理模式實作的基礎。這個接口我們命名為 Movie,代表電影播放的能力。

然後,我們要有一個真正的實作這個 Movie 接口的類,和一個隻是實作接口的代理類。

靜态代理和動态代理
靜态代理和動态代理

這個表示真正的影片。它實作了 Movie 接口,play() 方法調用時,影片就開始播放。那麼 Proxy 代理呢?

靜态代理和動态代理
靜态代理和動态代理

Cinema 就是 Proxy 代理對象,它有一個 play() 方法。不過調用 play() 方法時,它進行了一些相關利益的處理,那就是廣告。現在,我們編寫測試代碼。

靜态代理和動态代理
靜态代理和動态代理

然後觀察結果:

現在可以看到,代理模式可以在不修改被代理對象的基礎上,通過擴充代理類,進行一些功能的附加與增強。值得注意的是,代理類和被代理類應該共同實作一個接口,或者是共同繼承某個類。

上面介紹的是靜态代理的内容,為什麼叫做靜态呢?因為它的類型是事先預定好的,比如上面代碼中的 Cinema 這個類。下面要介紹的内容就是動态代理。

既然是代理,那麼它與靜态代理的功能與目的是沒有差別的,唯一有差別的就是動态與靜态的差别。

那麼在動态代理的中這個動态展現在什麼地方?

上一節代碼中 Cinema 類是代理,我們需要手動編寫代碼讓 Cinema 實作 Movie 接口,而在動态代理中,我們可以讓程式在運作的時候自動在記憶體中建立一個實作 Movie 接口的代理,而不需要去定義 Cinema 這個類。這就是它被稱為動态的原因。

也許概念比較抽象。現在執行個體說明一下情況。

假設有一個大商場,商場有很多的櫃台,有一個櫃台賣茅台酒。我們進行代碼的模拟。

靜态代理和動态代理
靜态代理和動态代理

SellWine 是一個接口,你可以了解它為賣酒的許可證。

靜态代理和動态代理
靜态代理和動态代理

然後建立一個類 MaotaiJiu,對的,就是茅台酒的意思。

我們還需要一個櫃台來賣酒:

靜态代理和動态代理
靜态代理和動态代理

GuitaiA 實作了 InvocationHandler 這個類,這個類是什麼意思呢?大家不要慌張,待會我會解釋。

然後,我們就可以賣酒了。

靜态代理和動态代理
靜态代理和動态代理

這裡,我們又接觸到了一個新的概念,沒有關系,先别管,先看結果。

看到沒有,我并沒有像靜态代理那樣為 SellWine 接口實作一個代理類,但最終它仍然實作了相同的功能,這其中的差别,就是之前讨論的動态代理所謂“動态”的原因。

放輕松,下面我們開始講解文法,文法非常簡單。

動态代碼涉及了一個非常重要的類 Proxy。正是通過 Proxy 的靜态方法 newProxyInstance 才會動态建立代理。

下面講解它的 3 個參數意義。

loader 自然是類加載器

interfaces 代碼要用來代理的接口

h 一個 InvocationHandler 對象

初學者應該對于 InvocationHandler 很陌生,我馬上就講到這一塊。

InvocationHandler 是一個接口,官方文檔解釋說,每個代理的執行個體都有一個與之關聯的 InvocationHandler 實作類,如果代理的方法被調用,那麼代理便會通知和轉發給内部的 InvocationHandler 實作類,由它決定處理。

InvocationHandler 内部隻是一個 invoke() 方法,正是這個方法決定了怎麼樣處理代理傳遞過來的方法調用。

proxy 代理對象

method 代理對象調用的方法

args 調用的方法中的參數

因為,Proxy 動态産生的代理會調用 InvocationHandler 實作類,是以 InvocationHandler 是實際執行者。

靜态代理和動态代理
靜态代理和動态代理

GuitaiA 就是實際上賣酒的地方。

現在,我們加大難度,我們不僅要賣茅台酒,還想賣五糧液。

靜态代理和動态代理
靜态代理和動态代理

Wuliangye 這個類也實作了 SellWine 這個接口,說明它也擁有賣酒的許可證,同樣把它放到 GuitaiA 上售賣。

靜态代理和動态代理
靜态代理和動态代理

我們來看結果:

有人會問,dynamicProxy 和 dynamicProxy1 什麼差別沒有?他們都是動态産生的代理,都是售貨員,都擁有賣酒的技術證書。

我現在擴大商場的經營,除了賣酒之外,還要賣煙。

首先,同樣要建立一個接口,作為賣煙的許可證。

然後,賣什麼煙呢?我是湖南人,那就芙蓉王好了。

靜态代理和動态代理
靜态代理和動态代理

然後再次測試驗證:

靜态代理和動态代理
靜态代理和動态代理

然後,檢視結果:

靜态代理和動态代理
靜态代理和動态代理

結果符合預期。大家仔細觀察一下代碼,同樣是通過 Proxy.newProxyInstance() 方法,卻産生了 SellWine 和 SellCigarette 兩種接口的實作類代理,這就是動态代理的魔力。

一定有同學對于為什麼 Proxy 能夠動态産生不同接口類型的代理感興趣,我的猜測是肯定通過傳入進去的接口然後通過反射動态生成了一個接口執行個體。

比如 SellWine 是一個接口,那麼 Proxy.newProxyInstance() 内部肯定會有

這樣相同作用的代碼,不過它是通過反射機制建立的。那麼事實是不是這樣子呢?直接檢視它們的源碼好了。需要說明的是,我目前檢視的源碼是 1.8 版本。

靜态代理和動态代理
靜态代理和動态代理

newProxyInstance 的确建立了一個執行個體,它是通過 cl 這個 Class 檔案的構造方法反射生成。cl 由 getProxyClass0() 方法擷取。

靜态代理和動态代理
靜态代理和動态代理

直接通過緩存擷取,如果擷取不到,注釋說會通過 ProxyClassFactory 生成。

靜态代理和動态代理
靜态代理和動态代理

這個類的注釋說,通過指定的 ClassLoader 和 接口數組 用工廠方法生成 proxy class。 然後這個 proxy class 的名字是:

是以,動态生成的代理類名稱是包名+$Proxy+id序号。

生成的過程,核心代碼如下:

這兩個方法,我沒有繼續追蹤下去,defineClass0() 甚至是一個 native 方法。我們隻要知道,動态建立代理這回事就好了。

現在我們還需要做一些驗證,我要檢測一下動态生成的代理類的名字是不是包名+$Proxy+id序号。

靜态代理和動态代理
靜态代理和動态代理

結果如下:

靜态代理和動态代理
靜态代理和動态代理

SellWine 接口的代理類名是:<code>com.sun.proxy.$Proxy0</code>

SellCigarette 接口的代理類名是:<code>com.sun.proxy.$Proxy1</code>

這說明動态生成的 proxy class 與 Proxy 這個類同一個包。

下面用一張圖讓大家記住動态代理涉及到的角色。

紅框中 <code>$Proxy0</code> 就是通過 Proxy 動态生成的。

<code>$Proxy0</code> 實作了要代理的接口。

<code>$Proxy0</code> 通過調用 <code>InvocationHandler</code> 來執行任務。

可能有同學會問,已經學習了代理的知識,但是,它們有什麼用呢?

主要作用,還是在不修改被代理對象的源碼上,進行功能的增強。

這在 AOP 面向切面程式設計領域經常見。

在軟體業,AOP為Aspect Oriented Programming的縮寫,意為:面向切面程式設計,通過預編譯方式和運作期動态代理實作程式功能的統一維護的一種技術。AOP是OOP的延續,是軟體開發中的一個熱點,也是Spring架構中的一個重要内容,是函數式程式設計的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,進而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。 主要功能 日志記錄,性能統計,安全控制,事務處理,異常處理等等。

上面的引用是百度百科對于 AOP 的解釋,至于,如何通過代理來進行日志記錄功能、性能統計等等,這個大家可以參考 AOP 的相關源碼,然後仔細琢磨。

同注解一樣,很多同學可能會有疑惑,我什麼時候用代理呢?

這取決于你自己想幹什麼。你已經學會了文法了,其他的看業務需求。對于實作日志記錄功能的架構來說,正合适。

至此,靜态代理和動态代理者講完了。

代理分為靜态代理和動态代理兩種。

靜态代理,代理類需要自己編寫代碼寫成。

動态代理,代理類通過 Proxy.newInstance() 方法生成。

不管是靜态代理還是動态代理,代理與被代理者都要實作兩樣接口,它們的實質是面向接口程式設計。

靜态代理和動态代理的差別是在于要不要開發者自己定義 Proxy 類。

動态代理通過 Proxy 動态生成 proxy class,但是它也指定了一個 InvocationHandler 的實作類。

代理模式本質上的目的是為了增強現有代碼的功能。

轉載來自:https://www.cnblogs.com/cC-Zhou/p/9525638.html

11