最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于JDK動態(tài)代理原理解析

 更新時間:2023年05月05日 08:59:45   作者:wen-pan  
這篇文章主要介紹了基于JDK動態(tài)代理原理解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

1、回顧一下JDK動態(tài)代理的核心參數(shù)

如果我們要為target類創(chuàng)建一個【JDK動態(tài)代理對象】,那么我們必須要傳入如下三個核心參數(shù)

  • 加載target類的類加載器
  • target類實現(xiàn)的接口
  • InvocationHandler

為什么必須要這三個參數(shù)呢?之前使用動態(tài)代理的時候都是直接按接口要求傳這三個參數(shù),但從來沒想過為什么?下面仔細去探究一下

2、實現(xiàn)一個簡單的動態(tài)代理

【JDK動態(tài)代理】的核心其實是借助【Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)】方法,去創(chuàng)建的動態(tài)代理對象,我們這里也使用這個方法去創(chuàng)建一個簡單的【動態(tài)代理對象】以便于理解他的核心原理。

①、定義接口Subject

public interface Subject {
    /**
     * 接口方法
     */
    void doSomething();
    /**
     * sayHello
     *
     * @param name name
     * @return string
     */
    String sayHello(String name);
}

②、定義接口的實現(xiàn)類RealSubject

實現(xiàn)Subject接口,并實現(xiàn)接口相關方法

public class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("RealSubject do something");
    }
    @Override
    public String sayHello(String name) {
        System.out.println("RealSubject sayHello");
        return "hello-" + name;
    }
}

③、定義代理對象創(chuàng)建工廠

  • 定義一個工廠類,該工廠類用于為target對象生產(chǎn)代理對象
  • 該工廠類借助Proxy.newProxyInstance來為目標對象創(chuàng)建代理對象
public class JdkDynamicProxyFactory {
    /**
     * 創(chuàng)建target類的代理對象
     * 注意:當調(diào)用代理對象中的方法時,其實就是調(diào)用的InvocationHandler里面的invoke方法,然后在invoke方法里調(diào)用目標對象對應的方法
     *
     * @param <T> 泛型
     * @return 代理對象
     */
    public static <T> T getProxy(Object target) {
        // 創(chuàng)建代理實例,分別傳入:【加載target類的類加載器、target類實現(xiàn)的接口、InvocationHandler】
        Object proxyInstance = Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("執(zhí)行目標方法前");
                        // 執(zhí)行目標方法
                        Object result = method.invoke(target, args);
                        System.out.println("執(zhí)行目標方法后");
                        // 返回目標方法的執(zhí)行結果
                        return result;
                    }
                });
        // 返回代理對象
        return (T) proxyInstance;
    }
}

④、創(chuàng)建測試類,為target創(chuàng)建代理對象

該測試類會將內(nèi)存中的動態(tài)代理對象保存到磁盤上,以便于我們后續(xù)分析生成的動態(tài)代理類的具體結構

public class Client {
    public static void main(String[] args) {
        // 保存生成的代理類的字節(jié)碼文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        // 目標對象
        RealSubject target = new RealSubject();
        // 使用JDK動態(tài)代理為【target對象】創(chuàng)建代理對象
        Subject proxy = JdkDynamicProxyFactory.getProxy(target);
        // 調(diào)用代理對象的方法
        proxy.doSomething();
        System.out.println("=====================華麗的分割線=====================");
        proxy.sayHello("wenpan");
    }
}

3、分析生成的動態(tài)代理類的結構

在上面一步中我們使用

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

將動態(tài)生成的代理保存到了磁盤上,下面我們就具體看看生成的代理類長什么樣

  • 可以看到代理類【繼承了Proxy類】,并且【實現(xiàn)了目標接口Subject】,覆寫了接口中的【每一個方法】
  • 這也說明了為什么JDK代理需要實現(xiàn)接口,因為Java是單繼承的,既然代理類繼承了Proxy類那么就無法再繼承其他類了
  • 在代理類中將我們的【目標接口Subject】的【所有方法(包括object父類的方法)】都以【靜態(tài)屬性的形式】保存了起來(主要是為了方便后面的【反射調(diào)用】)
  • 在調(diào)用動態(tài)代理對象的某個方法時(比如:調(diào)用doSomething方法),實質(zhì)上是調(diào)用的【Proxy類】的【h屬性】的invoke方法
  • 所以我們要重點去看看這個【Proxy.h】到底是個什么鬼?其實他就是創(chuàng)建代理對象是我們傳入的【InvocationHandler】
// 1、代理類首先繼承了Proxy類(這也說明了為什么JDK代理需要實現(xiàn)接口,因為Java是單繼承的),并且實現(xiàn)了目標接口Subject
public final class $Proxy0 extends Proxy implements Subject {
  	// 2、可以看到,在代理類中將我們的【目標接口Subject】的【所有方法(包括object父類的方法)】都以【靜態(tài)屬性的形式】保存了起來
    private static Method m1;
    private static Method m3;
    private static Method m4;
    private static Method m2;
    private static Method m0;
  	// 以靜態(tài)代碼塊的形式為屬性賦值
    static {
          try {
              m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
              m3 = Class.forName("com.stone.design.mode.proxy.jdk.Subject").getMethod("doSomething");
              m4 = Class.forName("com.stone.design.mode.proxy.jdk.Subject").getMethod("sayHello", Class.forName("java.lang.String"));
              m2 = Class.forName("java.lang.Object").getMethod("toString");
              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 $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
  	// 3、object父類的equals方法
    public final boolean equals(Object var1) throws  {
        try {
          	// 這里的supper是指的Proxy類,調(diào)用【Proxy類】的【h屬性】的invoke方法執(zhí)行
          	// 重點:【注意這里的super.h】其實就是我們創(chuàng)建代理對象是傳入的【InvocationHandler】,不信往后面看
          	// 這里也就體現(xiàn)了創(chuàng)建代理對象時為什么需要傳入【InvocationHandler】,以及為什么調(diào)用代理對象的方法時都是執(zhí)行的invoke方法
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
  	// 4、目標接口的方法
    public final void doSomething() throws  {
        try {
          	// 調(diào)用了【Proxy.h屬性】的invoke方法
          	// 注意這里的super.h】其實就是我們創(chuàng)建代理對象是傳入的【InvocationHandler】,不信往后面看
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
  	// 5、目標接口的方法
    public final String sayHello(String var1) throws  {
        try {
          	// 調(diào)用了【Proxy.h屬性】的invoke方法
          	// 注意這里的super.h】其實就是我們創(chuàng)建代理對象是傳入的【InvocationHandler】,不信往后面看
            return (String)super.h.invoke(this, m4, 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);
        }
    }
}

4、Proxy.h是什么鬼

在上面的第三點中我們看到,執(zhí)行代理對象的方法的時候其實質(zhì)是調(diào)用的【super.h.invoke方法】也就是【Proxy.h.invoke方法】,那么我們仔細看看【Proxy.h】到底是什么鬼

①、Proxy.newProxyInstance是如何創(chuàng)建代理對象的

下面的源代碼我做了一些刪減,只留下了最核心的部分

  • 通過下面代碼我們就明確了使用【newProxyInstance】方法創(chuàng)建代理對象時所做的幾件事情
  • 首先通過【目標接口】 + 【類加載器】創(chuàng)建一個Proxy類的【Class對象】
  • 然后通過這個【Class對象】獲取到他的有參構造器,并且傳入我們自定義的【InvocationHandler】作為構造函數(shù)參數(shù),并且通過反射的方式調(diào)用有參構造器創(chuàng)建一個【Proxy對象】
  • 在【Proxy的有參構造器】中,會將傳入的【InvocationHandler】保存到 【h屬性】上(方便后面的supper.h.invoke調(diào)用)
  • 代理對象創(chuàng)建完畢
public class Proxy implements java.io.Serializable {
  	// h屬性,保存我們傳遞進來的InvocationHandler
    protected InvocationHandler h;
    // 【有參構造器】注意這里的參數(shù)
    protected Proxy(InvocationHandler h) {
      Objects.requireNonNull(h);
      this.h = h;
    }
    // 生成代理對象的方法
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
        throws IllegalArgumentException{
      	// 1、InvocationHandler強制不允許為空
        Objects.requireNonNull(h);
        // 獲取到目標接口
        final Class<?>[] intfs = interfaces.clone();
        /*
         * Look up or generate the designated proxy class.
         * 2、獲取到代理類的Class對象(也就是Proxy)
         */
        Class<?> cl = getProxyClass0(loader, intfs);
        /*
         * Invoke its constructor with the designated invocation handler.
         * 通過反射執(zhí)行 cl 的有參構造,也就是下面這個,可以看到通過反射執(zhí)行Proxy有參構造,
         * 將InvocationHandler賦值到了h屬性上
         */
        try {
            // 3、獲取到有參構造器
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            // 4、通過構造器來創(chuàng)建一個代理對象并返回,這里傳入的參數(shù)h 就是我們的【InvocationHandler】
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            // 省略....
        }
    }
}

5、問題總結

現(xiàn)在再來看上面拋出的三個問題!

為什么創(chuàng)建代理對象時需要傳入如下三個參數(shù):

  • 加載target類的類加載器
  • target類實現(xiàn)的接口
  • InvocationHandler

①、為什么要傳入類加載器

因為動態(tài)代理類也是類,我們普通的類是從【磁盤上的.class文件(也可以是其他地方,比如網(wǎng)絡上)】里加載而來,而動態(tài)代理類則是在【運行過程中動態(tài)生成的類】。

那么既然是類那么他就一定要【被類加載器加載后】才能被我們的【Java虛擬機】識別。

所以我們會傳入【加載target類的類加載器】,用該類加載器來加載【動態(tài)生成的代理類】

②、為什么要傳入target類實現(xiàn)的接口

為啥要傳入【target類實現(xiàn)的接口】呢?直接【繼承目標類】不行嗎?肯定不行

  • 從上面的【動態(tài)生成的代理類的結構】來看,代理類繼承了Proxy類,由于【Java是單繼承】的,所以無法再通過繼承的方式來繼承【目標類】了。
  • 所以動態(tài)代理類需要【實現(xiàn)目標接口】,來重寫接口的方法

③、為什么要傳入InvocationHandler

  • 從【動態(tài)生成的代理類的結構】可以看出,我們傳入的InvocationHandler最終會被作為一個屬性保存到Proxy對象的【h屬性】上
  • 并且【動態(tài)代理對象】會覆寫【目標接口的所有方法】,在方法中會使用 supper.h.invoke的方式調(diào)用InvocationHandler的invoke方法,所以我們需要傳入InvocationHandler
  • 動態(tài)代理的每個方法調(diào)用都會先走InvocationHandler.invoke()方法

總結

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

最新評論

尖扎县| 山阳县| 闸北区| 体育| 施秉县| 如东县| 寻乌县| 丹巴县| 迭部县| 佳木斯市| 杨浦区| 福州市| 甘南县| 德兴市| 乐业县| 大荔县| 九寨沟县| 阿克陶县| 葫芦岛市| 中牟县| 平顺县| 德惠市| 乌海市| 乐都县| 高要市| 阿城市| 朝阳县| 瓮安县| 浏阳市| 七台河市| 深水埗区| 永德县| 洪泽县| 凤冈县| 北宁市| 古蔺县| 潢川县| 两当县| 枣阳市| 格尔木市| 华容县|