大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

專注Java教育14年 全國咨詢/投訴熱線:400-8080-105
動力節點LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁 學習攻略 Java學習 Java代理模式和動態代理詳細信息

Java代理模式和動態代理詳細信息

更新時間:2022-06-27 10:55:18 來源:動力節點 瀏覽1311次

Java的動態代理模式在實踐中的使用場景非常廣泛,比如大部分場景的Spring AOP、Java注解的獲取、日志記錄、用戶認證等等。動力節點小編帶你了解代理模式、靜態代理、原生動態基于JDK的代理。

代理模式

無論你是學習靜態代理還是動態代理,我們都需要先了解代理模型。

先看百度百科的定義:

代理模式的定義:為其他對象提供一個代理來控制對這個對象的訪問。在某些情況下,一個對象不合適或不能直接引用另一個對象,而代理對象可以充當客戶端和目標對象之間的中介。

直接看定義可能有些難以理解,下面就用生活中的具體例子來說明吧。

我們都去超市買過貨,超市從廠家買后賣給我們。我們通常不知道貨物要經過多少道工序才能到達超市。

在這個過程中,意味著制造商“委托”超市銷售商品,而我們(實物)是看不見的。超市(代理對象)充當制造商的“代理人”與我們互動。

同時,超市還可以根據具體的銷售情況進行折扣處理,豐富代理商的功能。

使用代理模型,我們可以做兩件事:

1.隱藏委托類的實現。

2. 將客戶端與委托類解耦,在不改變委托類代碼的情況下增加一些額外的功能(日志、權限)。

代理模式角色定義

在編程過程中,我們可以定義三種類型的對象:

Subject(抽象主題角色):定義代理類和真實主題的公共外部方法,也是代理類代理真實主題的一種方式。例如:廣告、銷售等。

RealSubject:真正實現業務邏輯的類。例如Vendor,實現廣告、銷售等方法。

代理:用于代理和封裝真實的主題。例如廣告和銷售等超時也實現。

上述三個角色的類圖如下:

靜態代理實例

靜態代理是指在程序運行之前已經存在代理類,這種情況下代理類通常是在Java代碼中定義的。

讓我們用一個具體的例子來演示一個靜態代理。

首先定義一組接口Sells,提供廣告、銷售等功能。然后提供Vendor類(制造商,代理對象)和Shop(超市,代理類),分別實現Sell接口。

Sell 接口定義如下:

/**
 * 委托和代理類都實現了 Sell 接口
 * @作者秒
 * @版本 1.0
 * @日期 2020/3/21 上午 9:30
 **/
公共接口賣{
    /**
     * 賣
     */
    無效出售();
    /**
     * 廣告
     */
    無效廣告();
}

Vendor 類定義如下:

/**
 * 供應商
 * @作者秒
 * @版本 1.0
 * @日期 2020/3/21 上午 9:30
 **/
公共類供應商實現 Sell{
    @覆蓋
    公共無效出售(){
        System.out.println("店鋪賣貨");
    }
    @覆蓋
    公共無效廣告(){
        System.out.println("店鋪廣告商品");
    }
}

Shop 類定義如下:

/**
 * 超市、代理商
 * @作者秒
 * @版本 1.0
 * @日期 2020/3/21 上午 9:30
 **/
公共類 Shop 實現 Sell{
    私人出售出售;
    公共商店(賣賣){
        this.sell = 賣;
    }
    @覆蓋
    公共無效出售(){
        System.out.println("代理類店鋪,處理銷售");
        出售.sell();
    }
    @覆蓋
    公共無效廣告(){
        System.out.println("代理類店鋪,處理廣告");
        sell.ad();
    }
}

代理類Shop通過聚合持有對代理類Vendor的引用,并在對應的方法中調用對應的Vendor方法。我們可以在hop類中增加一些額外的處理,比如過濾購買用戶、記錄日志等。

讓我們看看代理類是如何在客戶端中使用的。

/**
 * 靜態代理類測試方法
 * @作者秒
 * @版本 1.0
 * @日期 2020 年 3 月 21 日上午 9 點 33 分
 **/
公共類靜態代理 {
    公共靜態無效主要(字符串[]參數){
        // 供應商 - 代理類
        供應商 vendor = new Vendor();
        // 創建供應商的代理類 Shop
        賣賣=新店(供應商);
        // 客戶端正在使用代理類 Shop。
        sell.ad();
        出售.sell();
    }
}

在上面的代碼中,客戶看到的是Sell接口提供的功能,這個功能是Shop提供的。我們可以在不影響代理類Vendor的情況下,對hop進行修改或添加一些東西。

靜態代理的缺點

靜態代理實現簡單,不會侵入原始代碼,但是當場景復雜時,靜態代理有以下缺點:

1.當需要代理多個類時,代理對象實現與目標對象一致的接口。或者,只維護一個代理類來實現多個接口,但這會導致代理類變得太大。或者,創建多個新的代理類,但這會導致代理類過多。

2.當一個接口需要增加、刪除或修改方法時,目標對象和代理類都需要同時修改,不易維護。

因此,動態代理就派上用場了。

動態代理

動態代理是指在程序運行時創建代理類的方式。這種情況下,代理類不是在Java代碼中定義的,而是在運行時根據Java代碼中的指令動態生成的。

動態代理相對于靜態代理的優勢在于,很容易統一代理類的功能,而不需要修改各個代理類的功能。

基于JDK的原生動態代理的實現

動態代理的實現通常有兩種方式:JDK原生動態代理和CGLIB動態代理。這里我們以JDK的原生動態代理為例。

JDK動態代理主要涉及兩個類:java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler。

InvocationHandler 接口定義了以下方法:

/**
 * 呼叫處理程序
 */
公共接口 InvocationHandler {
    對象調用(對象代理,方法方法,對象[] args);
}

顧名思義,實現這個接口的中介類被用作“調用處理器”。當一個代理類對象的方法被調用時,這個“調用”被轉發給invoke方法。代理類對象作為代理參數傳入。參數method標識調用了代理類的哪個方法,args是方法的參數。這樣對代理類中所有方法的調用都變成invoke調用,可以給invoke方法(或不同的方法)添加統一的處理邏輯代理類方法可以根據方法參數進行不同的處理)。

Proxy 類用于獲取與指定代理對象關聯的調用處理程序。

下面以添加日志為例演示動態代理。

導入 java.lang.reflect.InvocationHandler;
導入java.lang.reflect.Method;
導入 java.util.Date;
公共類 LogHandler 實現 InvocationHandler {
    對象目標;// 代理對象,實際方法執行者
    公共日志處理程序(對象目標){
        this.target = 目標;
    }
    @覆蓋
    公共對象調用(對象代理,方法方法,對象 [] args)拋出 Throwable {
        前();
        對象結果 = method.invoke(target, args); // 調用目標的methodmethod方法
        后();
        返回結果;// 返回方法的執行結果
    }
    // 在調用方法調用之前執行
    私人無效之前(){
        System.out.println(String.format("日志開始時間[%s]", new Date()));
    }
    //調用invoke方法后執行
    私人無效后(){
        System.out.println(String.format("日志結束時間[%s]", new Date()));
    }
}

客戶端編寫者使用動態代理代碼如下:

導入 java.lang.reflect.Proxy;
/**
 * 動態代理測試
 *
 * @作者秒
 * @版本 1.0
 * @日期 2020/3/21 上午 10:40
 **/
公共類 DynamicProxyMain {
    公共靜態無效主要(字符串[]參數){
        // 創建中介類的實例
        LogHandler logHandler = new LogHandler(new Vendor());
        // 設置此變量以默認名稱保存動態代理類 $Proxy0.class
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        // 獲取代理類實例 Sell
        Sell sell = (Sell) (Proxy.newProxyInstance(Sell.class.getClassLoader(), new Class[]{Sell.class}, logHandler));
        // 通過代理類對象調用代理類方法實際上是去invoke方法調用
        出售.sell();
        sell.ad();
    }
}

執行后,打印日志如下:

方法sell的調用日志處理

店鋪賣貨

方法銷售的記錄

日志處理調用方法廣告

店鋪廣告商品

調用方法廣告的日志處理

經過上面的驗證,我們發現我們已經成功的為我們的代理類在方法執行前后添加了日志。

為了查看上面示例中生成的動態代理類的代碼,我們添加了以下屬性設置(需要在生產環境中刪除)。

// 設置此變量以默認名稱保存動態代理類 $Proxy0.class
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

所以,我們在執行main方法之后,也生成了一個名為$Proxy0.class的類文件。通過反編譯,可以看到如下代碼:

包 com.sun.proxy;
進口com.choupangxia.proxy.Sell;
導入 java.lang.reflect.InvocationHandler;
導入java.lang.reflect.Method;
導入 java.lang.reflect.Proxy;
導入 java.lang.reflect.UndeclaredThrowableException;
公共最終類 $Proxy0 擴展代理實現 Sell {
    私有靜態方法 m1;
    私有靜態方法 m2;
    私有靜態方法 m4;
    私有靜態方法 m3;
    私有靜態方法 m0;
    公共 $Proxy0(InvocationHandler var1) 拋出 {
        超級(var1);
    }
    public final boolean equals(Object var1) throws {
        嘗試 {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } 捕捉(運行時異常 | 錯誤 var3){
            拋出 var3;
        } 捕捉(Throwable var4){
            拋出新的 UndeclaredThrowableException(var4);
        }
    }
    公共最終字符串 toString() 拋出 {
        嘗試 {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } 捕捉(運行時異常 | 錯誤 var2){
            拋出 var2;
        } 捕捉(Throwable var3){
            拋出新的 UndeclaredThrowableException(var3);
        }
    }
    公共最終無效廣告()拋出{
        嘗試 {
            super.h.invoke(this, m4, (Object[])null);
        } 捕捉(運行時異常 | 錯誤 var2){
            拋出 var2;
        } 捕捉(Throwable var3){
            拋出新的 UndeclaredThrowableException(var3);
        }
    }
    公共最終無效出售()拋出{
        嘗試 {
            super.h.invoke(this, m3, (Object[])null);
        } 捕捉(運行時異常 | 錯誤 var2){
            拋出 var2;
        } 捕捉(Throwable var3){
            拋出新的 UndeclaredThrowableException(var3);
        }
    }
    公共最終 int hashCode() 拋出 {
        嘗試 {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } 捕捉(運行時異常 | 錯誤 var2){
            拋出 var2;
        } 捕捉(Throwable var3){
            拋出新的 UndeclaredThrowableException(var3);
        }
    }
    靜止的 {
        嘗試 {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.choupangxia.proxy.Sell").getMethod("ad");
            m3 = Class.forName("com.choupangxia.proxy.Sell").getMethod("sell");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } 捕捉(NoSuchMethodException var2){
            拋出新的 NoSuchMethodError(var2.getMessage());
        } 捕捉(ClassNotFoundException var3){
            拋出新的 NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到$Proxy0(代理類)繼承了Proxy類,實現了所有被代理的接口,以及equals、hashCode、toString等方法。

由于動態代理類繼承了 Proxy 類,因此每個代理類都與一個 InvocationHandler 方法調用處理器相關聯。

類和所有方法都用public final修飾,所以只能使用代理類,不能再繼承。

每個方法都有一個 Method 對象,該對象在靜態代碼塊中創建并以“m+number”格式命名。

通過 super.h.invoke(this,m1,(Object[])null); 調用該方法 而super.h.invoke其實是一個LogHandler對象,在創建代理時傳遞給Proxy.newProxyInstance。它繼承 InvocationHandler 類并負責實際的調用處理邏輯。

以上就是關于“Java代理模式和動態代理詳細信息”的介紹,如果大家對此比較感興趣,想了解更多相關知識,可以關注一下動力節點的Java設計模式,里面有更豐富的知識等著大家去學習,希望對大家能夠有所幫助哦。

提交申請后,顧問老師會電話與您溝通安排學習

免費課程推薦 >>
技術文檔推薦 >>
主站蜘蛛池模板: 你懂的国产 | 伊在人香蕉99久久 | 性xxx免费视频| 国产综合久久久久 | 免费观看日本a毛片 | 免费视频不卡 | 新久草在线视频 | 亚洲欧美中日韩 | 亚洲精品视频免费观看 | 狠狠ri| 四虎免费播放观看在线视频 | 久久久久免费精品视频 | 欧美视频日韩专区午夜 | 久久综合久色欧美婷婷 | 波多野结衣一区二区 | 国产日本三级 | 久久久久久久亚洲精品 | 国产 日韩 欧美 亚洲 | 亚洲国产成人麻豆精品 | 国产欧美国产精品第二区 | 91久久精品午夜一区二区 | 久久久精品国产免费观看同学 | 免费看成人毛片日本久久 | 亚洲欧美一区二区三区在饯 | 成人免费在线视频 | 草草草在线视频 | 99精品视频在线观看免费专区 | 国产第一页视频 | 日本特级视频 | 久久精品香蕉视频 | 国产精品毛片一区 | 色视频一区 | 香港之夜免费观看 | 在线观看免费情网站大全 | 国产一区二区精品久 | 在线观看视频一区 | 一级免费毛片 | 中文字幕一区二区三区视频在线 | 一本久道久久综合狠狠爱 | 中文字幕亚洲综合久久202 | 黄片毛片免费观看 |