Java動態代理模式淺析

来源:https://www.cnblogs.com/nwgdk/archive/2018/04/09/dynamicproxy.html
-Advertisement-
Play Games

"Java代理設計模式 靜態代理" "Java中的動態代理 調用處理器" 代理設計模式的UML圖: 我將首先介紹Java中的各種代理實現方法 Java代理設計模式 靜態代理 這個例子非常簡單,只有一個方法 的介面 : 測試代碼: 測試輸出: 現在麻煩的是,Jerry的領導因為團隊中的開發者像Jerr ...


代理設計模式的UML圖:

clipboard1-28

我將首先介紹Java中的各種代理實現方法

Java代理設計模式 - 靜態代理

這個例子非常簡單,只有一個方法wirteCode的介面IDeveloper:

public interface IDeveloper {
    public void writeCode();
}
// 實現這個介面的類:
public class Developer implements IDeveloper{
    private String name;
    public Developer(String name){
        this.name = name;
    }
    @Override
    public void writeCode() {
        System.out.println("Developer " + name + " writes code");
    }
}

測試代碼:

public class DeveloperTest {
    public static void main(String[] args) {
        IDeveloper jerry = new Developer("Jerry");
        jerry.writeCode();
    }
}

測試輸出:

Developer Jerry writes code

現在麻煩的是,Jerry的領導因為團隊中的開發者像Jerry一樣沒有編寫技術文檔,所以並不滿意。經過討論後,整個團隊達成協議,相關文檔必須與代碼一起提供。

為了迫使開發人員編寫文檔而不直接對現有的實現類Developer進行修改,現在就可以使用靜態代理來實現:

// 創建一個和Developer類實現同樣介面的類
public class DeveloperProxy implements IDeveloper{
    private IDeveloper developer;
    // 引用Developer類對象
    public DeveloperProxy(IDeveloper developer){
        this.developer = developer;
    }
    @Override
    public void writeCode() {
        System.out.println("Write documentation...");
        this.developer.writeCode();
    }
}

測試代碼:

public class DeveloperTest {
    public static void main(String[] args) {
        Developer jerry = new Developer("jerry");
        DeveloperProxy jerryproxy = new DeveloperProxy(jerry);
        jerryproxy.writeCode();
    }
}

測試輸出:

Write documentation...
Developer jerry writes code

靜態代理的優點

假設你希望在不修改原始類代碼的情況下增強現有的穩定實現,你可以創建一個代理類,並將原始實現封裝為代理中的私有屬性。增強的功能是在代理類中完成的,對現有的代碼是完全透明的。回到上面的示例,客戶端代碼並不關心它用來調用writeCode()方法的變數是否指向真正的開發人員或開發人員代碼。

clipboard1-29

優點:

  1. 易於實施和理解
  2. 原始實現與其代理之間的關係在編譯時已經確定,運行時沒有額外的開銷。

靜態代理的缺點

我們仍然使用這個例子來說明。
假設現在缺失文檔的問題在QA同事中仍然存在。如果我們想通過靜態代理來解決這個問題,那麼必須引入另一個代理類。

這是測試人員的介面:

public interface ITester {
    public void doTesting();
}
// ITester 介面的實現類:
public class Tester implements ITester {
    private String name;

    public Tester(String name){
        this.name = name;
    }
    @Override
    public void doTesting() {
        System.out.println("Tester " + name + " is testing code");
    }
}

測試人員代理:

public class TesterProxy implements ITester{
    private ITester tester;
    public TesterProxy(ITester tester){
        this.tester = tester;
    }
    @Override
    public void doTesting() {
        System.out.println("Tester is preparing test documentation...");
        tester.doTesting();
    }
}

測試代碼和輸出:

clipboard1-30

從Tester代理的源代碼中我們可以很容易的觀察到它與開發人員具有完全相同的邏輯。如果又過了一段時間,我們必須為軟體交付過程中的其他同事建立文檔,我們必須一次又一次的引入新的靜態代理類,這會導致靜態代理類變得十分龐大。

Java中的動態代理 - 調用處理器

現在我通過代理類EnginnerProxy來為所有的具體角色提供代理服務,而不是單獨為每個原始實現類設置專用的靜態代理類。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class EnginnerProxy implements InvocationHandler
{
    Object obj;
    public Object bind(Object obj)
    {
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
                .getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
    {
        System.out.println("Enginner writes document");
        Object res = method.invoke(obj, args);
        return res;
    }
}

主要筆記:

  1. 不是從具有業務介面(IDeveloper或ITester)的專用介面繼承,而是在此變體中,通過代理繼承自JDK提供的技術介面InvocationHandler
  2. 為了確保通用代理可以適用於所有可能的具體實現類,在代理中定義了具有泛型類型的Object變數。
  3. 調用代理實例的介面方法時,它將被InvocationHandler中定義的invoke方法攔截,其中由應用程式開發人員聲明的增強邏輯與由Java Reflection調用的原始邏輯一起調用。

下麵是如何使用InvocationHandler設計的動態代理和測試輸出:

clipboard1-31

動態代理類的限制

雖然這個變體成功的避免了靜態代理中的重覆缺陷,但是它仍然有一個局限性,它無法使用不是從介面繼承的實現類,就是說,使用動態代理類,原始類必須先要實現一個或多個介面,這個介面也就是代理介面。

考慮下麵的例子,產品所有者沒有實現任何介面:

public class ProductOwner {
    private String name;
    public ProductOwner(String name){
        this.name = name;
    }
    public void defineBackLog(){
        System.out.println("PO: " + name + " defines Backlog.");
    }
}

以下代碼在IDE中沒有任何語法錯誤:

ProductOwner po = new ProductOwner("Ross");
ProductOwner poProxy = (ProductOwner) new EnginnerProxy().bind(po);
poProxy.defineBackLog();

不幸的是編譯時報出了以下錯誤:

clipboard1-32


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、基本用法 你可以用 v-model 指令在表單 <input> 及 <textarea> 元素上創建雙向數據綁定。 但 v-model 本質上不過是語法糖。它負責監聽用戶的輸入事件以更新數據,並對一些極端場景進行一些特殊處理。 v-model 會忽略所有表單元素的 value、checked、s ...
  • 參考書《ECMAScript 6入門》http://es6.ruanyifeng.com/Set和Map數據結構1.Set 基本用法 Set是一種新的數據結構,它的成員都是唯一的不重覆的。 let s1 = new Set(); s1.add({"name":"123"}); s1 // Set(1 ...
  • 轉載請註明出處:http://www.cnblogs.com/Joanna-Yan/p/8745794.html Nginx是一個高性能的HTTP伺服器/反向代理伺服器及電子郵件(IMAP/POP3)代理伺服器。其占有記憶體少,併發能力強,在同類型的網頁伺服器中表現較好。Nginx可以在大多數Unix ...
  • 1.map集合遍歷方式 :keyset()方法 得到map集合中所有的鍵,裝到set集合中;然後可通過set集合作遍歷。 public class mapdemo { public static void main(String[] args) { //1.調用map集合的方法keyset,把所有的 ...
  • 花費二個多月的時間編寫了可以實時模擬工廠產品生產流程的程式,工廠產品生產流程的模擬,就是計算在工藝文件所規定的工序下,不同種類的多件產品(同一類別的產品可以有多件)在不同類別的多台設備(同一類別的設備可以有多台)上全部生產完畢所需的總時間。每一件產品可以在生產流程中先後多次在同一類設備上生產而且生產 ...
  • 兩年前在做Java EE開發平臺時,因為用戶登錄相關的模塊是委托給另一位同事完成的,所以雖然知道大體概念,但是對客戶端怎麼安全傳輸密碼到服務端的具體細節並不甚瞭解。然而這次在做4A系統(認證、授權、監控、審計)時,無論怎樣都繞不過這一塊內容了,於是在仔細研究了一下之前的方案,並參考網上的一些資料後, ...
  • Python文件處理 Python文件處理 在python中,要對一個文件進行操作,得把文件抽象為Streams流或者說file object或者叫file-like objects。 這樣將文件當作一個流對象來處理就方便多了。Stream對象提供了很多操作方法(如read(),write()等), ...
  • spring MVC框架 一、什麼是spring MVC Spring MVC屬於SpringFrameWork的後續產品,已經融合在Spring Web Flow裡面。Spring 框架提供了構建 Web 應用程式的全功能 MVC 模塊。使用 Spring 可插入的 MVC 架構,從而在使用Spr ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...