中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中Proxy動態代理機制的示例分析

發布時間:2021-06-28 11:44:12 來源:億速云 閱讀:153 作者:小新 欄目:開發技術

小編給大家分享一下Java中Proxy動態代理機制的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

一、Jvm加載對象

在說Java動態代理之前,還是要說一下Jvm加載對象的過程,這個依舊是理解動態代理的基礎性原理:

Java中Proxy動態代理機制的示例分析

Java類即源代碼程序.java類型文件,經過編譯器編譯之后就被轉換成字節代碼.class類型文件,類加載器負責讀取字節代碼,并轉換成java.lang.Class對象,描述類在元數據空間的數據結構,類被實例化時,堆中存儲實例化的對象信息,并且通過對象類型數據的指針找到類。

過程描述:源碼->.java文件->.class文件->Class對象->實例對象

所以通過New創建對象,獨斷其背后很多實現細節,理解上述過程之后,再了解一個常用的設計模式,即代理模式。

二、代理模式

2.1、基本描述

代理模式給某一個(目標)對象提供一個代理對象,并由代理對象持有目標對象的引用。所謂代理,就是一個對象代表另一個對象執行相應的動作程序。而代理對象可以在客戶端和目標對象之間起到中介的作用。

Java中Proxy動態代理機制的示例分析

代理模式在實際的生活中場景很多,例如中介、律師、代購等行業,都是簡單的代理邏輯,在這個模式下存在兩個關鍵角色:

目標對象角色:即代理對象所代表的對象。

代理對象角色:內部含有目標對象的引用,可以操作目標對象;AOP編程就是基于這個思想。

2.2、靜動態模式

  • 靜態代理:在程序運行之前確定代理角色,并且明確代理類和目標類的關系。

  • 動態代理:基于Java反射機制,在JVM運行時動態創建和生成代理對象。

三、靜態代理

基于上述靜態代理的概念,用一段代碼進行描述實現,基本邏輯如下:

  • 明確目標對象即被代理的對象;

  • 定義代理對象,通過構造器持有目標對象;

  • 代理對象中定義前后置增強方法;

目標對象與前后置增強代碼就組成了代理對象,這樣就不用直接訪問目標對象,像極了電視劇中那句話:我是律師,我的當事人不方便和你對話。

public class Proxy01 {
    public static void main(String[] args) {
        TargetObj targetObj = new TargetObj() ;
        ProxyObj proxyObj = new ProxyObj(targetObj) ;
        proxyObj.invoke();
    }
}
class TargetObj {
    public void execute (){
        System.out.println("目標類方法執行...");
    }
}
class ProxyObj {
    private TargetObj targetObj ;
    /**
     * 持有目標對象
     */
    public ProxyObj (TargetObj targetObj){
        this.targetObj = targetObj ;
    }
    /**
     * 目標對象方法調用
     */
    public void invoke (){
        before () ;
        targetObj.execute();
        after () ;
    }
    /**
     * 前后置處理
     */
    public void before (){
        System.out.println("代理對象前置處理...");
    }
    public void after (){
        System.out.println("代理對象后置處理...");
    }
}

靜態代理明確定義了代理對象,即有一個代理對象的.java文件加載到JVM的過程,很顯然的一個問題,在實際的開發過程中,不可能為每個目標對象都定義一個代理類,同樣也不能讓一個代理對象去代理多個目標對象,這兩種方式的維護成本都極高。

代理模式的本質是在目標對象的方法前后置入增強操作,但是又不想修改目標類,通過前面反射機制可以知道,在運行的時候可以獲取對象的結構信息,基于Class信息去動態創建代理對象,這就是動態代理機制。

順便說一句:技術的底層實現邏輯不好理解是眾所周知,然而基礎知識點并不復雜,例如代理模式的基本原理,但是結合到實際的復雜應用中(AOP模式),很難活靈活現的理解到是基于反射和動態代理的方式實現的。

四、動態代理

4.1、場景描述

基于一個場景來描述動態代理和靜態代理的區別,即最近幾年很火的概念,海外代購:

Java中Proxy動態代理機制的示例分析

在代購剛興起的初期,是一些常去海外出差的人,會接代購需求,即代理人固定;后來就興起海外代購平臺,海淘等一系列產品,即用戶代購需求(目標對象)由代購平臺去實現,但是具體誰來操作這個就看即時分配,這個場景與動態代理的原理類似。

4.2、基礎API案例

首先看兩個核心類,這里簡述下概念,看完基本過程再細聊:

Proxy-創建代理對象,核心參數:

  • ClassLoader:(目標類)加載器;

  • Interfaces:(目標類)接口數組;

  • InvocationHandler:代理調用機制;

InvocationHandler-代理類調用機制:

  • invoke:這個上篇說的反射原理;

  • method:反射類庫中的核心API;

目標對象和接口

interface IUser {
    Integer update (String name) ;
}
class UserService implements IUser {
    @Override
    public Integer update(String name) {
        Integer userId = 99 ;
        System.out.println("UserId="+userId+";updateName="+name);
        return userId ;
    }
}

代理對象執行機制

class UserHandler implements InvocationHandler {
    private Object target ;
    public UserHandler (Object target){
        this.target = target ;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before()...");
        Object result = method.invoke(target, args);
        System.out.println("after()...");
        return result;
    }
}

具體組合方式

public class Proxy02 {
    public static void main(String[] args) {
        /*
         * 生成$Proxy0的class文件
         */
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        /*
         * 目標對象信息
         */
        IUser userService = new UserService();
        ClassLoader classLoader = userService.getClass().getClassLoader();
        Class<?>[] interfaces = UserService.class.getInterfaces() ;
        /*
         * 創建代理對象
         */
        InvocationHandler userHandler = new UserHandler(userService);
        /*
         * 代理類對象名
         * proxyClassName=com.java.proxy.$Proxy0
         */
        String proxyClassName = Proxy.newProxyInstance(classLoader,interfaces,userHandler).getClass().getName();
        System.out.println("proxyClassName="+proxyClassName);
        /*
         * 具體業務實現模擬
         */
        IUser proxyUser1 = (IUser) Proxy.newProxyInstance(classLoader,interfaces,userHandler);
        IUser proxyUser2 = (IUser) Proxy.newProxyInstance(classLoader,interfaces,userHandler);
        proxyUser1.update("cicada") ;
        proxyUser2.update("smile") ;
    }
}

這里之所以要生成代理類的結構信息,因為從JVM加載的過程看不到相關內容,關鍵信息再次被獨斷:

javap -v Proxy02.class

Java中Proxy動態代理機制的示例分析

查看代理類名稱

/*
 * proxyClassName=com.java.proxy.$Proxy0
 */
String proxyClassName = Proxy.newProxyInstance(classLoader,interfaces,userHandler).getClass().getName();
System.out.println("proxyClassName="+proxyClassName);

下意識輸出代理對象名稱,這里即對應JVM機制,找到Class對象名,然后分析結構,這樣就明白動態代理具體的執行原理了。

生成代理類.class文件

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

通過上面JVM加載對象的機制可知,描述代理類的Class對象一定存在,只是在運行時并沒有生成顯式的.class文件,通過上面生成代理類.class的語法,會在項目目錄的/com/java/proxy路徑下創建文件。

順便說一句:作為一只程序員,復雜總是和我們環環相繞,說好的簡單點呢?

4.3、代理類結構

繼承與實現

class $Proxy0 extends Proxy implements IUser {}

從代理類的功能來思考,可以想到需要繼承Proxy與實現IUser接口,還有就是持有調用機制的具體實現類,用來做業務增強。

構造方法

public $Proxy0(InvocationHandler var1) throws  {
    super(var1);
}

通過構造方法,持有UserHandler具體的執行機制對象。

接口實現

final class $Proxy0 extends Proxy implements IUser {
    private static Method m3;
    public final Integer update(String var1) throws  {
        try {
            return (Integer)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
}

目標類的基本需求update()方法,通過代理類進行承接,并基于UserHandler實現具體的增強業務處理。

基礎方法

final class $Proxy0 extends Proxy implements IUser {
    private static Method m0;
    private static Method m1;
    private static Method m2;
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.java.proxy.IUser").getMethod("update", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
}

基于Object類,定義Java中幾個常用方法equals()判斷,toString()方法,hashCode()值,這個在分析Map源碼的時候有說過為什么這幾個方法通常都是一起出現。

4.4、JDK源碼

上面是案例執行的過程和原理,還有一個關鍵點要明白,即JDK源碼的邏輯:

IUser proxyUser = (IUser) Proxy.newProxyInstance(classLoader,interfaces,userHandler);

Proxy提供的靜態方法newProxyInstance(),通過各個參數的傳入,構建一個新的代理Class對象,即$Proxy0類的結構信息,這里再回首看下三個核心參數:

  • ClassLoader:基于JVM運行過程,所以需要獲取目標類UserService的類加載器;

  • Interfaces:目標類UserService實現的接口,從面向對象來考慮,接口與實現分離,代理類通過實現IUser接口,模擬目標類的需求;

  • InvocationHandler:代理類提供的功能封裝即UserHandler,可以在目標方法調用前后做增強處理;

以上是“Java中Proxy動態代理機制的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

五原县| 兴安县| 车致| 蓬安县| 蒙阴县| 清河县| 肃南| 泽库县| 凯里市| 南岸区| 满洲里市| 长武县| 邳州市| 咸阳市| 通化县| 米易县| 旬阳县| 西宁市| 运城市| 乌拉特中旗| 吉安县| 渑池县| 拉孜县| 武胜县| 宜章县| 周口市| 公主岭市| 铁力市| 前郭尔| 高邑县| 文山县| 炎陵县| 常山县| 平谷区| 揭西县| 昆山市| 冕宁县| 景东| 麻城市| 元阳县| 清苑县|