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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...