java中的代理模式

来源:http://www.cnblogs.com/bloglkl/archive/2017/01/25/6350153.html
-Advertisement-
Play Games

代理模式在java中的使用,包括靜態代理和動態代理,對retrofit進行模擬 ...


代理模式

在某些情況下,一個客戶不想或者不能直接引用一個對象,此時可以通過一個稱之為“代理”的第三者來實現間接引用。代理對象可以在客戶端和目標對象之間起到 中介的作用,並且可以通過代理對象去掉客戶不能看到 的內容和服務或者添加客戶需要的額外服務。

簡單來說代理模式就是通過一個代理對象去訪問一個實際對象,並且可以像裝飾模式一樣給對象添加一些功能。

靜態代理

所謂靜態代理即在程式運行前代理類就已經存在,也就是說我們編寫代碼的時候就已經把代理類的代碼寫好了,而動態代理則是在程式運行時自動生成代理類。

描述起來太過抽象,看一下代碼就明白是怎麼回事了

  • main
public class Main {

    public static void main(String[] args) {
        Water water = new Water();
        WaterProxy waterProxy = new WaterProxy(water);
        waterProxy.drink();
    }

}
  • 介面
//代理類與被代理類共同實現的介面
public interface Drink {
    void drink();
}
  • 被代理類
//被代理的類
public class Water implements Drink {

    @Override
    public void drink() {
        System.out.println("drink water");
    }

}
  • 代理類
//代理類
//與被代理類實現同一個介面
public class DrinkProxy implements Drink {
    
    private Drink drinkImpl;
    
    //通過構造函數傳入Water對象
    public DrinkProxy(Drink drinkImpl) {
        this.drinkImpl = drinkImpl;
    }
    
    @Override
    public void drink() {
        //在執行被代理對象的方法前做一些事情
        System.out.println("before drink");
        //執行被代理對象的方法
        drinkImpl.drink();
        //在執行被代理對象的方法後做一些事
        System.out.println("after drink");
    }

}

執行結果

before drink
drink water
after drink

動態代理

有時候我們只想改變代理類所代理的類,但是代理對象執行實際對象的方法前後所做的事情是一樣的,正所謂鐵打的代理類,流水的被代理類。而採用靜態代理就只能代理實現了同一介面的類,如果要代理任意類則必須寫很多重覆的代理類。此時我們可以採用動態代理,java已經為實現動態代理提供了一套比較方便的工具。

  • java.lang.reflect.Proxy類中可以動態生成代理對象的方法
  /**
     *返回實現了指定介面的對象,調用代理對象的方法會調用 
     *InvocationHandler的invoke方法
     *
     * @param   loader 獲取代理類所使用的類載入器
     * @param   interfaces 代理類所要實現的介面
     * @param   h 實現了InvocationHandler介面的對象
     * @return  代理對象
     */
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)
  • InvocationHandler介面
/**
 *每個代理類都有一個關聯的InvocationHandler
 *當代理對象執行一個方法的時候會直接執行invoke方法
 */
public interface InvocationHandler {

    /**
     * @param   調用該方法的代理對象
     * @param   method 代理對象所調用的方法
     * @param   args 調用的方法的參數
     * @return  調用的方法的返回值
     */
    public Object invoke(Object proxy, Method method, Object[] args)
}

描述總是比較抽象,還是看實際例子比較好理解

例子

  • InvocationHandler介面的實現類
public class CommonInvocationHandler implements InvocationHandler {
    
    //被代理的對象
    private Object proxied;
    
    public CommonInvocationHandler(Object proxied) {
        this.proxied = proxied;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在調用被代理對象的方法前做一些事情
        System.out.println("before doing something");
        //調用被代理對象的方法
        Object result = method.invoke(proxied, args);
        //在調用被代理對象的方法後做一些事情
        System.out.println("after doing something");;
        return result;
    }

}
  • Main

public class Main {

    public static void main(String[] args) {
        //被代理的對象
        Water water = new Water();
        //動態獲取代理對象
        Drink waterProxy = 
                (Drink) Proxy.newProxyInstance(water.getClass().getClassLoader(),
                        water.getClass().getInterfaces(), 
                        new CommonInvocationHandler(water));
        //通過代理對象調用方法
        waterProxy.drink();
    }

}
  • 輸出結果
before doing something
drink water
after doing something

也可以不要具體的被代理對象,但是必須有相應的介面(沒有實現介面的類可以使用cglib實現動態代理)才可以動態獲取代理對象。像最近比較火的Retrofit就直接通過聲明好的介面使用動態代理進行網路請求。

例子

簡單的模擬一下retrofit

  • POST註解
//Post請求註解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface POST {
  String value() default "";
}
  • Query註解
//Post請求註解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface POST {
  String value() default "";
}
  • Service介面
public interface Service {
    //用POST註解聲明請求的方式和相對路徑
    @POST("/login")
    //@Query註解聲明請求的參數名
    void login(@Query("username")String username, 
            @Query("password")String password);
}
  • Main
public class Main {

    public static void main(String[] args) {
        // 動態獲取Service介面的代理
        Service service = (Service) Proxy.newProxyInstance(Service.class.getClassLoader(),
                new Class[] { Service.class }, new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 通過註解獲取請求的相對路徑
                        String retativePath = ((POST) method.getAnnotations()[0]).value();
                        System.out.println("relative path: " + retativePath);
                        // 獲取參數的註解
                        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
                        // 通過參數的註解獲取請求參數
                        for (int i = 0; i < parameterAnnotations.length; i++) {
                            if (parameterAnnotations[i].length != 0) {
                                for (int j = 0; j < parameterAnnotations[i].length; j++) {
                                    Query query = (Query) parameterAnnotations[i][j];
                                    System.out.println(query.value() + ": " + args[i].toString());
                                }
                            }
                        }
                        return null;
                    }
                });
        // 調用代理對象的方法
        service.login("hello", "world");
    }

}

參考

JAVA動態代理

代理模式



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

-Advertisement-
Play Games
更多相關文章
  • 1. 點擊File->New->Other,在彈出的對話框中選擇Maven->Maven Project: 2. 點擊Next,選擇maven-archetype-webapp: 3. 填入Group ID和Artifact ID,會自動生成一個包名: 4. 點擊Finish,會生成以下的目錄: 5 ...
  • 1. 首先下載apache-maven-3.3.9-bin.zip,並解壓; 2. 添加系統變數MAVEN_HOME,值為apache-maven-3.3.9-bin.zip的解壓路徑: 再在path變數中添加: 3. 輸入命令mvn -version檢測安裝是否成功: 4. 編輯%MAVEN_HO ...
  • 首先,在applicationContext.xml文件中加一行: 加上這一行以後,將自動掃描路徑下麵的包,如果一個類帶了@Service註解,將自動註冊到Spring容器,不需要再在applicationContext.xml文件定義bean了,類似的還包括@Component、@Reposito ...
  • FunDA的設計目標就是把後臺資料庫中的數據搬到記憶體里,然後進行包括並行運算的數據處理,最後可能再對後臺資料庫進行更新。如果需要把數據搬到記憶體的話,那我們就必須考慮記憶體是否能一次性容納所有的數據,有必要配合數據處理分部逐步讀入,這就是Reactive Stream規範主要目的之一。所以在設計FunD ...
  • 前段時間,聽了一堂C語言的課,那老師說:“數組名就是一個指向數組首地址的常量指針”。 我上百度查了一些,有好多教程、書籍等,都持相同的觀點。 但我一直感覺——數組名不等於指針。 實踐是檢驗真理的唯一標準,於此,有了以下內容。 首先,聲明一個數組和一個常量指針並指向那個數組。 設問:一個整型指針的長度 ...
  • 閱讀目錄 前言 解決數據一致性的方案 回到DDD 設計 實現 結語 一、前言 之前的十一篇把用戶購買商品並提交訂單整個流程上的中間環節都過了一遍。現在來到了這最後一個環節,提交訂單。單從業務上看,這個動作的背後包含了多個業務操作,根據用戶填寫的訂單信息生成訂單、扣除使用的餘額和積分、使用選擇的禮券等 ...
  • 不要誤會,我不是針對python,也不是針對nodejs,我是說除了.NET之外,玩爬蟲的都是垃圾 ...
  • ddd小白,一篇章節便能激起了心中漣漪,感慨之初,記於筆下。 第1章 消化知識 用醍醐灌頂、茅塞頓開來形容此章短短的文字,實不為過。 簡單介紹背景:旅游互聯網,B2B,初創公司。產品設計-代碼開發的銜接有過兩種明顯形式: 1. 項目的推進由產品部起頭,收集、分析、過濾需求,形成原型文檔(word,e ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...