當前位置:
首頁 > 知識 > Java靜態代理與動態代理模式的實現

Java靜態代理與動態代理模式的實現

前言: 在現實生活中,考慮以下的場景:小王打算要去租房,他相中了一個房子,準備去找房東洽談相關事宜。但是房東他很忙,平時上班沒時間,總沒有時間見面,他也沒辦法。後來,房東想了一個辦法,他找到了一個人代替自己和小王洽談,房東本人不用出面,他只要把他的對房客的要求告訴他找的那個人,那個人和你商量就可以了,這樣就可以完成租房這件事了。這種現實場景比比皆是,所呈現出來的其實就是代理模式的原型的一種。我們把焦點轉向編程,你是否在編程中經常遇見這樣一個問題,對於訪問某個對象,我們希望給它的方法前加入一個標記,比如對象的方法開始執行、結束等等(比如日誌記錄)。怎麼辦呢,這個時候只要我們編寫一個複製的類,然後把這個對象傳給這個類,再對這個類進行操作,不就可以了嗎。這就是代理模式,複製的類就是代理對象,通過代理對象與我們進行打交道就可以對它原來的對象進行改造。對於有些時候現有的對象不能滿足我們的需求的時候,如何對它進行擴展,對方法進行改造,使其適用於我們所面臨的問題,這就是代理模式的思維出發點。

一:代理模式的介紹

1.1:目標

為其他對象提供一種代理以控制對這個對象的訪問

解釋:在實際編程中我們會產生一個代理對象,然後去引用被代理對象,對被代理對象進行控制與訪問,實現客戶端對原代理對象的訪問,詳情見下面的代碼示例。

1.2:適用性

在需要用比較通用和複雜的對象指針代替簡單的指針的時候,使用Proxy 模式。下面是一 些可以使用Proxy 模式常見情況:

1.2.1:遠程代理(Remote Proxy )為一個對象在不同的地址空間提供局部代表。 NEXTSTEP[Add94] 使用N X P r o x y 類實現了這一目的。

1.2.2:虛代理(Virtual Proxy )根據需要創建開銷很大的對象。在動機一節描述的I m a g e P r o x y 就是這樣一種代理的例子。

1.2.3: 保護代理(Protection Proxy )控制對原始對象的訪問。保護代理用於對象應該有不同 的訪問許可權的時候

1.2.4: 智能指引(Smart Reference )取代了簡單的指針,它在訪問對象時執行一些附加操作。

1.3:結構

Java靜態代理與動態代理模式的實現

二:實現靜態代理

2.1:代碼場景

假如我們現在由以下的場景:文件編輯器要對一個圖像文件進行操作,遵循以下順序:載入,繪製,獲取長度和寬度,存儲四個步驟,但是有個問題,需要被載入的圖片非常大,每次載入的時候都要耗費很多時間。並且我們希望對圖片的操作可以記錄出來操作的步驟,比如第一步、第二步這樣便於我們去理解為了解決這個問題,我們可以先考慮解決第一個問題就是利用代理模式去新建一個代理對象,然後在代理對象里去實現一個緩存,這樣下次我們直接可以去緩存裡面取對象,而不用去新建,這樣就省去了新建對象消耗的資源。另一方面,我們可以考慮去引用原來的方法,再給這方法基礎上添加我們所要做的記錄。接下里我們用java代碼來實現這個場景:

2.2:代碼示範

2.2.1:首先新建一個介面,命名為Graphic,其中主要規範了我們進行操作的步驟

Java靜態代理與動態代理模式的實現

2.2.2:然後去新建一個Image類,用於實現介面,對操作進行具體控制,注意為了其中的Extent是對寬度和長度的封裝(省略get和set方法)

Java靜態代理與動態代理模式的實現

Java靜態代理與動態代理模式的實現

Java靜態代理與動態代理模式的實現

2.2.3:接下來就是很關鍵的一步了,新建我們的代理類,我們新建一個類叫做ImageProxy,然後實現緩存與記錄的效果:

Java靜態代理與動態代理模式的實現

Java靜態代理與動態代理模式的實現

Java靜態代理與動態代理模式的實現

2.2.4:我們的文檔編輯器現在要開始進行文檔編輯了,我們來實現具體的代碼,我們先來引用一下原對象,看一下原來的對象會出現什麼情況:

Java靜態代理與動態代理模式的實現

2.2.5:測試代碼

Java靜態代理與動態代理模式的實現

我們可以看出,它會消耗3秒才會出來具體的對象,並且沒有我們所需要的記錄。好了,我們把2.2.4的引用代碼改為: Graphic proxy = new ImageProxy();

我們再來測試一下:

Java靜態代理與動態代理模式的實現

很明顯可以看出,通過訪問我們的代理對象,就可以實現對原方法的改造,這就是代理模式的精髓思想。不過到這裡你可能會問,為什麼不對原對象進行改造呢?為什麼要給他新建一個代理對象,這不是很麻煩嗎。回答這個問題,首先要提一個代碼的設計原則,也就是有名的開閉原則:對擴展開放,對修改關閉。這句話的意思就是不建議對原有的代碼進行修改,我們要做的事就是盡量不用動原有的類和對象,在它的基礎上去改造,而不是直接去修改它。至於這個原則為什麼這樣,我想其中一個原因就是因為軟體體系中牽一髮很動全身的事情很常見,很可能你修改了這一小塊,然而與此相關的很多東西就會發生變化。所以輕易不要修改,而是擴展。

三:實現動態代理

3.1:靜態代理的不足:

通過看靜態代理可以動態擴展我們的對象,但是有個問題,在我們進行方法擴展的時候,比如我們的日誌功能:每個前面都得寫第一步、第二步。如果我們要再一些其他的東西,比如許可權校驗、代碼說明,一個兩個方法還好,萬一方法成百個呢,那我們豈不是要累死。這就是動態代理要解決的問題,只需要寫一次就可以,究竟是怎麼實現的呢,接下里我們來一探究竟吧。

3.2:動態代理的準備:

動態代理需要用到JDk的Proxy類,通過它的newProxyInstance()方法可以生成一個代理類,我們來通過jdk看一下具體的說明,如何使用它:

Java靜態代理與動態代理模式的實現

返回一個指定介面的代理類實例,該介面可以將方法調用指派到指定的調用處理程序。此方法相當於:

Java靜態代理與動態代理模式的實現

Proxy.newProxyInstance 拋出 IllegalArgumentException,原因與 Proxy.getProxyClass 相同。

    參數:

    loader - 定義代理類的類載入器

    interfaces - 代理類要實現的介面列表

    h - 指派方法調用的調用處理程序

    返回:

    一個帶有代理類的指定調用處理程序的代理實例,它由指定的類載入器定義,並實現指定的介面

    拋出:

    IllegalArgumentException - 如果違反傳遞到 getProxyClass 的參數上的任何限制

    NullPointerException - 如果 interfaces 數組參數或其任何元素為 null,或如果調用處理程序 h 為 null 從中可以看出它有三個參數,分別是classlcoder、interface、InvocationHandler.只要我們把這三個參數傳遞給他,它就可以 返回給我們一個代理對象,訪問這個代理對象就可以實現對原對象的擴展。接下來,我們用代碼來實現它。

    3.3:代碼場景

    我們來做這樣一個場景,我們實現一個計算器,計算器裡面有加減乘除方法,然後我們實現這個計算的介面,有具體的類和被代理的類,我們通過動態代理來生成代理類,而不用自己去建了,好了,看接下來的代碼:

    3.4:動態代理的代碼實現

    3.4.1:首先我們新建一個介面,命名為Calculator ,聲明四個方法:

    Java靜態代理與動態代理模式的實現

    3.4.2:新建一個實現類,命名為CalculatorImpl ,也就是被代理類

    Java靜態代理與動態代理模式的實現

    3.4.3:新建一個類,命名為CalCulatorDynamicProxy,也就是我們的代理類,用來對上面的類進行代理:

    Java靜態代理與動態代理模式的實現

    Java靜態代理與動態代理模式的實現

    這裡要特彆強調的問題就是:invoke()方法,注意其中的參數,分別是被代理對象、方法、和對象參數,這裡的原理是反射,通過獲取原對象的class對象,然後進行處理,我們可以通過method對象拿到被代理對象的方法,也是add()、mul()、sub()、div()方法,也可以通過args對象數組取得傳入的參數,比如我們具體傳入的數值,再通過method.invoke()方法進行調用,就進行了被代理對象的方法的執行,然後就是返回的結果(如果方法前為void,返回的就是null)

    3.4.4:我們來做具體的測試

    Java靜態代理與動態代理模式的實現

    具體的測試結果:

    Java靜態代理與動態代理模式的實現

    可以看出動態代理模式輕鬆完成了對被代理對象的日誌記錄功能,並且只用寫一次,這樣即便有成百上千的方法我們也不怕,這就是動態代理領先於靜態代理之處,雖然實現起來有點麻煩,但是其方便,動態的給被代理對象添加功能。我們所寫的重複代碼更少,做的事情更少。

    四:總結

    本文介紹了動態代理和靜態代理的概念,並對其進行了代碼實現,在實際的工作中,我們會經常遇到需要代理模式的地方,希望能多多思考,促進我們形成一定的思維模式。並且動態代理作為SpringAop的實現原理,封裝了動態代理,讓我們實現起來更加方便,對於這部分內容可以只做了解,理解其背後的運行機制即可,並不需要具體實現,如果需要實現,直接使用spring的Aop功能即可。

    希望看完本文,能對代理這種思維有深入的理解。好了,本文就講到這裡,謝謝。


    學習Java的同學注意了!!!

    學習過程中遇到什麼問題或者想獲取學習資源的話,歡迎加入Java學習交流群495273252,我們一起學Java!

    喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

    本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


    請您繼續閱讀更多來自 Java團長 的精彩文章:

    淺談Java反射機制
    JVM的內存區域劃分以及垃圾回收機制詳解
    優質免費在線學習網站
    深入理解Java常用類——StringBuilder
    Java——IO 流

    TAG:Java團長 |

    您可能感興趣

    反射與代理設計模式-動態代理設計模式
    CGLIB 與動態代理
    IIS實現反向代理
    基於 CGLIB 庫的動態代理機制
    致代理和新代理的你
    動態 | AQ 代理公告
    面對POS機代理行業的套路及潛規則,代理新手該如何選擇
    動態代理學習筆記 jdk vs cglib
    iWorker:互聯網時代管理軟體渠道代理變革
    優質的Facebook廣告代理幫你實現海外精準營銷
    如何構建自己的ip代理池
    BM:對代理如何運作和「dApp開發者」如何計費的理解可能需要調整
    精彩再現|恩芬怡&ICO代理商參觀體驗活動回顧
    Squid代理伺服器
    設計模式之代理模式
    甲骨文推出新的雲基礎設施代理 令Kubernetes開發人員的生活更輕鬆
    螞蟻金服Service Mesh新型網路代理的思考與實踐
    無需代理一樣可看youtube
    POS機代理好做嗎?
    滲透基礎——使用Go語言開發socks代理工具