免费观看又色又爽又黄的小说免费_美女福利视频国产片_亚洲欧美精品_美国一级大黄大色毛片

什么是JDK動態代理

本篇內容主要講解“什么是JDK動態代理”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“什么是JDK動態代理”吧!

林芝網站建設公司創新互聯公司,林芝網站設計制作,有大型網站制作公司豐富經驗。已為林芝上千多家提供企業網站建設服務。企業網站搭建\外貿營銷網站建設要多少錢,請找那個售后服務好的林芝做網站的公司定做!

JDK動態代理是指:代理類實例在程序運行時,由JVM根據反射機制動態的生成。也就是說代理類不是用戶自己定義的,而是由JVM生成的。

由于其原理是通過Java反射機制實現的,所以在學習前,要對反射機制有一定的了解。傳送門:Java反射機制:跟著代碼學反射

下面是本篇講述內容: 什么是JDK動態代理

1. JDK動態代理的核心類

JDK動態代理有兩大核心類,它們都在Java的反射包下(java.lang.reflect),分別為InvocationHandler接口和Proxy類。

1.1 InvocationHandler接口

代理實例的調用處理器需要實現InvocationHandler接口,并且每個代理實例都有一個關聯的調用處理器。當一個方法在代理實例上被調用時,這個方法調用將被編碼并分派到其調用處理器的invoke方法上。

也就是說,我們創建的每一個代理實例都要有一個關聯的InvocationHandler,并且在調用代理實例的方法時,會被轉到InvocationHandlerinvoke方法上。

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

invoke方法的作用是:處理代理實例上的方法調用并返回結果。

其有三個參數,分別為:

  • proxy:是調用該方法的代理實例。

  • method:是在代理實例上調用的接口方法對應的Method實例。

  • args:一個Object數組,是在代理實例上的方法調用中傳遞的參數值。如果接口方法為無參,則該值為null。

其返回值為:調用代理實例上的方法的返回值。

1.2 Proxy類

Proxy類提供了創建動態代理類及其實例的靜態方法,該類也是動態代理類的超類。

代理類具有以下屬性:

  • 代理類的名稱以 “$Proxy” 開頭,后面跟著一個數字序號。

  • 代理類繼承了Proxy類。

  • 代理類實現了創建時指定的接口(JDK動態代理是面向接口的)。

  • 每個代理類都有一個公共構造函數,它接受一個參數,即接口InvocationHandler的實現,用于設置代理實例的調用處理器。

Proxy提供了兩個靜態方法,用于獲取代理對象。

1.2.1 getProxyClass

用于獲取代理類的Class對象,再通過調用構造函數創建代理實例。

public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)
        throws IllegalArgumentException

該方法有兩個參數:

  • loader:為類加載器。

  • intefaces:為接口的Class對象數組。

返回值為動態代理類的Class對象。

1.2.2 newProxyInstance

用于創建一個代理實例。

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException

該方法有三個參數:

  • loader:為類加載器。

  • interfaces:為接口的Class對象數組。

  • h:指定的調用處理器。

返回值為指定接口的代理類的實例。

1.3 小結

Proxy類主要用來獲取動態代理對象,InvocationHandler接口主要用于方法調用的約束與增強。

2. 獲取代理實例的代碼示例

上一章中已經介紹了獲取代理實例的兩個靜態方法,現在通過代碼示例來演示具體實現。

2.1 創建目標接口及其實現類

JDK動態代理是基于接口的,我們創建一個接口及其實現類。

Foo接口:

public interface Foo {

    String ping(String name);

}

Foo接口的實現類RealFoo:

public class RealFoo implements Foo {

    @Override
    public String ping(String name) {
        System.out.println("ping");
        return "pong";
    }

}

2.2 創建一個InvocationHandler

創建一個InvocationHandler接口的實現類MyInvocationHandler。該類的構造方法參數為要代理的目標對象。

invoke方法中的三個參數上面已經介紹過,通過調用methodinvoke方法來完成方法的調用。

這里一時看不懂沒關系,后面源碼解析章節會進行剖析。

public class MyInvocationHandler implements InvocationHandler {

    // 目標對象
    private final Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy - " + proxy.getClass());
        System.out.println("method - " + method);
        System.out.println("args - " + Arrays.toString(args));
        return method.invoke(target, args);
    }
}

2.3 方式一:通過getProxyClass方法獲取代理實例

具體實現步驟如下:

  1. 根據類加載器和接口數組獲取代理類的Class對象

  2. 過Class對象的構造器創建一個實例(代理類的實例)

  3. 將代理實例強轉成目標接口Foo(因為代理類實現了目標接口,所以可以強轉)。

  4. 最后使用代理進行方法調用。

@Test
public void test1() throws Exception {
    Foo foo = new RealFoo();
    // 根據類加載器和接口數組獲取代理類的Class對象
    Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);

    // 通過Class對象的構造器創建一個實例(代理類的實例)
    Foo fooProxy = (Foo) proxyClass.getConstructor(InvocationHandler.class)
        .newInstance(new MyInvocationHandler(foo));

    // 調用 ping 方法,并輸出返回值
    String value = fooProxy.ping("楊過");
    System.out.println(value);

}

輸出結果:

proxy - class com.sun.proxy.$Proxy4
method - public abstract java.lang.String io.github.gozhuyinglong.proxy.Foo.ping(java.lang.String)
args - [楊過]
ping
pong

通過輸出結果可以看出:

  • 代理類的名稱是以$Proxy開頭的。

  • 方法實例為代理類調用的方法。

  • 參數為代理類調用方法時傳的參數。

2.4 方式二:通過newProxyInstance方法獲取代理實例

通過這種方法是最簡單的,也是推薦使用的,通過該方法可以直接獲取代理對象。

注:其實該方法后臺實現實際與上面使用getProxyClass方法的過程一樣。

@Test
public void test2() {
    Foo foo = new RealFoo();
    // 通過類加載器、接口數組和調用處理器,創建代理類的實例
    Foo fooProxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                                new Class[]{Foo.class},
                                                new MyInvocationHandler(foo));
    String value = fooProxy.ping("小龍女");
    System.out.println(value);
}

2.5 通過Lambda表達式簡化實現

其實InvocationHander接口也不用創建一個實現類,可以使用Lambad表達式進行簡化的實現,如下代碼:

@Test
public void test3() {
    Foo foo = new RealFoo();

    Foo fooProxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                                new Class[]{Foo.class},
                                                (proxy, method, args) -> method.invoke(foo, args));
    String value = fooProxy.ping("雕兄");
    System.out.println(value);
}

3. 源碼解析

3.1 代理類$Proxy是什么樣子

JVM為我們自動生成的代理類到底是什么樣子的呢?下面我們先來生成一下,再來看里面的構造。

3.1.1 生成$Proxy的.class文件

JVM默認不創建該.class文件,需要增加一個啟動參數: -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

在IDEA中點擊【Edit Configurations...】,打開 Run/Debug Configurations 配置框。

什么是JDK動態代理 將上面啟動參數加到【VM options】中,點擊【OK】即可。 什么是JDK動態代理

再次運行代碼,會在項目中的【com.sun.proxy】目錄中找到這個.class文件,我這里是“$Proxy4.class” 什么是JDK動態代理

3.1.2 為什么加上這段啟動參數就能生成$Proxy的字節碼文件

Proxy類中有個ProxyClassFactory靜態內部類,該類主要作用就是生成靜態代理的。

其中有一段代碼ProxyGenerator.generateProxyClass用來生成代理類的.class文件。 什么是JDK動態代理

其中變量saveGeneratedFiles便是引用了此啟動參數的值。將該啟動參數配置為true會生成.class文件。 什么是JDK動態代理

3.1.3 這個代理類$Proxy到底是什么樣子呢

神秘的面紗即將揭露,前面很多未解之迷在這里可以找到答案!

打開這個$Proxy文件,我這里生成的是$Proxy4,下面是內容:

// 該類為final類,其繼承了Proxy類,并實現了被代理接口Foo
public final class $Proxy4 extends Proxy implements Foo {

    // 這4個Method實例,代表了本類實現的4個方法
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    // 靜態代碼塊根據反射獲取這4個方法的Method實例
    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("io.github.gozhuyinglong.proxy.Foo").getMethod("ping");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

    // 一個公開的構造函數,參數為指定的 InvocationHandler 
    public $Proxy4(InvocationHandler var1) throws  {
        super(var1);
    }
    
    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);
        }
    }

    // Foo接口的實現方法,最終調用了 InvocationHandler 中的 invoke 方法
    public final String ping(String var1) throws  {
        try {
            return (String)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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);
        }
    }
}

通過該文件可以看出:

  • 代理類繼承了Proxy類,其主要目的是為了傳遞InvocationHandler

  • 代理類實現了被代理的接口Foo,這也是為什么代理類可以直接強轉成接口的原因。

  • 有一個公開的構造函數,參數為指定的InvocationHandler,并將參數傳遞到父類Proxy中。

  • 每一個實現的方法,都會調用InvocationHandler中的invoke方法,并將代理類本身、Method實例、入參三個參數進行傳遞。這也是為什么調用代理類中的方法時,總會分派到InvocationHandler中的invoke方法的原因。

3.2 代理類是如何創建的

我們從Proxy類為我們提供的兩個靜態方法開始getProxyClassnewProxyInstance。上面已經介紹了,這兩個方法是用來創建代理類及其實例的,下面來看源碼。

3.2.1 getProxyClass 和 newProxyInstance方法

什么是JDK動態代理

什么是JDK動態代理

通過上面源碼可以看出,這兩個方法最終都會調用getProxyClass0方法來生成代理類的Class對象。只不過newProxyInstance方法為我們創建好了代理實例,而getProxyClass方法需要我們自己創建代理實例。

3.2.2 getProxyClass0 方法

下面來看這個統一的入口:getProxyClass0 什么是JDK動態代理

從源碼和注解可以看出:

  • 代理接口的最多不能超過65535個

  • 會先從緩存中獲取代理類,則沒有再通過ProxyClassFactory創建代理類。(代理類會被緩存一段時間。)

3.2.3 WeakCache類

這里簡單介紹一下WeakCache<K, P, V> 類,該類主要是為代理類進行緩存的。獲取代理類時,會首先從緩存中獲取,若沒有會調用ProxyClassFactory類進行創建,創建好后會進行緩存。 什么是JDK動態代理

3.2.4 ProxyClassFactory類

ProxyClassFactoryProxy類的一個靜態內部類,該類用于生成代理類。下圖是源碼的部分內容:

什么是JDK動態代理

  • 代理類的名稱就是在這里定義的,其前綴是$Proxy,后綴是一個數字。

  • 調用ProxyGenerator.generateProxyClass來生成指定的代理類。

  • defineClass0方法是一個native方法,負責字節碼加載的實現,并返回對應的Class對象。

3.3 原理圖

為了便于記錄,將代理類的生成過程整理成了一張圖。

什么是JDK動態代理

到此,相信大家對“什么是JDK動態代理”有了更深的了解,不妨來實際操作一番吧!這里是創新互聯網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

當前名稱:什么是JDK動態代理
URL標題:http://newbst.com/article40/jhcgho.html

成都網站建設公司_創新互聯,為您提供定制網站域名注冊企業建站虛擬主機品牌網站制作網站改版

廣告

聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯

手機網站建設