設計模式之動態代理

来源:http://www.cnblogs.com/Dylansuns/archive/2017/06/11/6985711.html
-Advertisement-
Play Games

一、動態代理概念 動態代理分為JDK動態代理和cglib動態代理兩種方式。 jdk動態代理是由Java內部的反射機制來實現的,cglib動態代理底層則是藉助asm來實現的。 總的來說,反射機制在生成類的過程中比較高效,而asm在生成類之後的相關執行過程中比較高效(可以通過將asm生成的類進行緩存,這 ...


一、動態代理概念

動態代理分為JDK動態代理和cglib動態代理兩種方式。

jdk動態代理是由Java內部的反射機制來實現的,cglib動態代理底層則是藉助asm來實現的。

總的來說,反射機制在生成類的過程中比較高效,而asm在生成類之後的相關執行過程中比較高效(可以通過將asm生成的類進行緩存,這樣解決asm生成類過程低效問題)。

還有一點必須註意:jdk動態代理的應用前提,必須是目標類基於統一的介面。如果沒有上述前提,jdk動態代理不能應用。

由此可以看出,jdk動態代理有一定的局限性,cglib這種第三方類庫實現的動態代理應用更加廣泛,且在效率上更有優勢。

二、JDK動態代理

以下代碼使用代理模式實現一個大小寫字元轉換的功能。

定義介面和實現類:

ISomeService介面:

package com.ietree.basicskill.designpattern.dynamicproxy.jdk;

/**
 * 介面類
 * 
 * @author Root
 */
public interface ISomeService {
    
    String doFirst();
    
    void doSecond();
}

SomeServiceImpl實現類:

package com.ietree.basicskill.designpattern.dynamicproxy.jdk;

/**
 * 實現類
 * 
 * @author Root
 */
public class SomeServiceImpl implements ISomeService {

    @Override
    public String doFirst() {
        System.out.println("執行doFirst()...");
        String result = "abcde";
        return result;
    }

    @Override
    public void doSecond() {
        System.out.println("執行doSecond()...");
    }

}

JDK動態代理類:

package com.ietree.basicskill.designpattern.dynamicproxy.jdk;

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

public class Main {
    public static void main(String[] args) {

        final ISomeService target = new SomeServiceImpl();
        
        // 使用JDK的Proxy動態代理,要求目標類和代理類必須實現相同的介面,因為其底層的執行原理與靜態代理的相同
        ISomeService service = (ISomeService) Proxy.newProxyInstance(
                // 目標類的類載入器
                target.getClass().getClassLoader(),
                // 目標類所實現的所有介面
                target.getClass().getInterfaces(), 
                new InvocationHandler() {
                    // proxy:代理對象
                    // method:目標方法
                    // args:目標方法的參數列表
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 調用目標方法
                        Object result = method.invoke(target, args);
                        if (result != null) {
                            result = ((String) result).toUpperCase();
                        }
                        return result;
                    }
                });
        String result = service.doFirst();
        System.out.println(result);

        service.doSecond();
    }
}

三、cglib動態代理

Cglib是一個優秀的動態代理框架,它的底層使用ASM在記憶體中動態的生成被代理類的子類,使用CGLIB即使代理類沒有實現任何介面也可以實現動態代理功能。CGLIB具有簡單易用,它的運行速度要遠遠快於JDK的Proxy動態代理:

CGLIB的核心類:
    net.sf.cglib.proxy.Enhancer – 主要的增強類
    net.sf.cglib.proxy.MethodInterceptor – 主要的方法攔截類,它是Callback介面的子介面,需要用戶實現
    net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method類的代理類,可以方便的實現對源對象方法的調用,如使用:
    Object o = methodProxy.invokeSuper(proxy, args);//雖然第一個參數是被代理對象,也不會出現死迴圈的問題。

net.sf.cglib.proxy.MethodInterceptor介面是最通用的回調(callback)類型,它經常被基於代理的AOP用來實現攔截(intercept)方法的調用。這個介面只定義了一個方法
public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;

第一個參數是代理對像,第二和第三個參數分別是攔截的方法和方法的參數。原來的方法可能通過使用java.lang.reflect.Method對象的一般反射調用,或者使用 net.sf.cglib.proxy.MethodProxy對象調用。net.sf.cglib.proxy.MethodProxy通常被首選使用,因為它更快。

以下程式實現了大小寫轉換的功能:

實現類SomeService:

package com.ietree.basicskill.designpattern.dynamicproxy.cglib;

/**
 * 實現類
 * 
 * @author Root
 */
public class SomeService {

    public String doFirst() {
        System.out.println("執行doFirst()...");
        String result = "abcde";
        return result;
    }

    public void doSecond() {
        System.out.println("執行doSecond()...");
    }

}

代理類MyCglibFactory:

package com.ietree.basicskill.designpattern.dynamicproxy.cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class MyCglibFactory implements MethodInterceptor {

    private SomeService target;
    
    public MyCglibFactory() {
        super();
        target = new SomeService();
    }

    public SomeService myCglibCreator() {
        // 創建增強器對象
        Enhancer enhancer = new Enhancer();
        // 指定目標類,即父類
        enhancer.setSuperclass(SomeService.class);
        // 設置回調介面對象
        enhancer.setCallback(this);

        return (SomeService) enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 調用目標方法
        Object result = method.invoke(target, args);
        if (result != null) {
            result = ((String) result).toUpperCase();
        }
        return result;
    }

}

測試:

package com.ietree.basicskill.designpattern.dynamicproxy.cglib;

public class Main {
    public static void main(String[] args) {

        SomeService service = new MyCglibFactory().myCglibCreator();
        
        String result = service.doFirst();
        System.out.println("result = " + result);

        service.doSecond();
    }
}

運行結果:

執行doFirst()...
result = ABCDE
執行doSecond()...

 


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

-Advertisement-
Play Games
更多相關文章
  • ASP.NET Core 中使用 MySql,如果欄位類型為 ,不管設置多少長度,插入或更新數據的時候,會自動截斷(截取 255 長度的字元)。 出現問題的原因,就是使用了 程式包(我使用的版本是 ),可能是其版本問題,升級版本的話,可能問題不會出現了。 解決方式 :將 MySql 所有欄位類型為 ...
  • 廢話不多說! 一下是 .NET core 和 .NET framework 速度對比。 兩者使用最慢的冒泡排序演算法: 排序10萬條數據 平均下來.net core 需要39秒, 而.net fw 平均49秒。 在同等條件下 .net core 性能領先。 這隻是個簡單得測試。我相信.net core ...
  • 在面試的時候經常會被問到,委托和事件的聯繫和區別?之前也一直沒有徹底搞明白,下麵就來總結一下。 從一個有趣的需求入手。有三個角色,貓,老鼠和主人,當貓叫的時候,老鼠開始逃跑,主人則從睡夢中驚醒。 使用事件實現 如下代碼: 通過demo可以總結: 1,定義和使用事件的流程,如下圖: 2,定義事件參數要 ...
  • 目標 瞭解Python的歷史 瞭解Python的特征 瞭解Python的應用 掌握Linux下Python開發環境的搭建 理解Windows下Python環境搭建 案例 安裝Python,寫出第一個Python程式 第一節 Python簡史 什麼是Python 一種解釋型的、面向對象的、帶有動態語義 ...
  • API(Application Programming Interface):應用程式編程介面 使用Scanner 獲取鍵盤錄入的字元串 next() ; 在遇到空格的時候 會判定為當前的輸入結束 空格之後的內容會收不到 nextLine(); 可以避免被空格中斷 , 但是在於接收數字一起使用的時候 ...
  • 枚舉是常用的功能,看看Python的枚舉. 枚舉的定義 註意: 定義枚舉時,成員名稱不允許重覆 預設情況下,不同的成員值允許相同。但是兩個相同值的成員,第二個成員的名稱被視作第一個成員的別名 如果枚舉中存在相同值的成員,在通過值獲取枚舉成員時,只能獲取到第一個成員 我們就獲得了Month類型的枚舉類 ...
  • 轉自:http://www.cnblogs.com/kristain/articles/2033566.html ...
  • 設計模式既上一篇關於單例模式後,終於要接著寫下去了,先來個最簡單的,簡單工廠模式,這個設計模式很簡單,也是最常用的(是不是好多東西都這樣,越簡單,門檻越低,越討人喜歡)。 概念(原諒我無恥的抄百度百科): 簡單工廠模式是屬於創建型模式,又叫做靜態工廠方法(Static Factory Method) ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...