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

Java中的動態(tài)代理使用

 更新時間:2024年07月12日 11:13:03   作者:讓你三行代碼QAQ  
這篇文章主要介紹了Java中的動態(tài)代理使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

動態(tài)代理

動態(tài)代理的時候,定義一個接口,需要代理人和被代理類實現(xiàn)這個接口,這樣不夠靈活,代理類能夠代理的類只有實現(xiàn)這個接口的類。

非常不靈活,假如被代理人的類沒有實現(xiàn)這個接口,那么就需重新寫一個代理類。對于日志、事務(wù)這些操作是不區(qū)分業(yè)務(wù)的,即不需要規(guī)定都實現(xiàn)某接口。因此,出現(xiàn)了動態(tài)代理

java種的動態(tài)代理生成的方式大概有三種JDK動態(tài)代理、instrument動態(tài)代理、cglib動態(tài)代理。其中,前兩種是JDK自帶的,cglib是需要第三方依賴使用的。

  • JDK動態(tài)代理:在程序運行的過程種動態(tài)的生成代理類,這個代理類實現(xiàn)被代理類的接口,因此使用時候被代理類必須實現(xiàn)接口;
  • instrument動態(tài)代理:是通過Class字節(jié)文件在加載至內(nèi)存的過程種添加攔截器,動態(tài)的修改字節(jié)文件實現(xiàn)的;
  • cglib:在程序運行的過程種動態(tài)生成代理類,這個代理類是通過繼承被代理類實現(xiàn)的,因此不能代理被final修飾的類
  • JDK動態(tài)代理和cglib動態(tài)代理的底層都是ASM;

JDK動態(tài)代理

先看這樣一個程序

/** 接口*/
public interface Mobile {
    void move();
}
/** 被代理類*/
public class Car implements Mobile{
    @Override
    public void move() {
        System.out.println("move...");
    }
}
/** 使用動態(tài)代理*/
public class Test {
    public static void main(String[] args) {
        Car car = new Car();
        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles",true);
        Mobile proxyInstance = (Mobile) Proxy.newProxyInstance(Car.class.getClassLoader(), Car.class.getInterfaces(),  new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                long before = System.currentTimeMillis();
                System.out.println(method.getName()+"方法被執(zhí)行前。。。");
                Object invoke = method.invoke(car, args);
                System.out.println(method.getName()+"方法被執(zhí)行后。。。");
                long after = System.currentTimeMillis();
                System.out.println("耗時:" + (after - before));
                return null;
            }
        });

        proxyInstance.move();
    }
}

創(chuàng)建代理類需要調(diào)用Proxy.newProxyInstance()方法,其中有三個參數(shù)

  • 第一個參數(shù):一個類加載器,一般使用被代理類的類加載器
  • 第二個參數(shù):被代理類實現(xiàn)的接口
  • 第三個參數(shù):一個InvocationHandler接口的實現(xiàn)類

可以把動態(tài)代理的創(chuàng)建改成這樣:自己寫一個類實現(xiàn)InvocationHandler。

public class Test {
    public static void main(String[] args) {
        Car car = new Car();
        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
        Mobile proxyInstance = (Mobile) Proxy.newProxyInstance(Car.class.getClassLoader(), Car.class.getInterfaces(), new MyInvocationHandler(car));
        proxyInstance.move();
    }

}
class MyInvocationHandler implements InvocationHandler{
    private Car car;

    public MyInvocationHandler(Car car) {
        this.car = car;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long before = System.currentTimeMillis();
        System.out.println(method.getName()+"方法被執(zhí)行前。。。");
        Object invoke = method.invoke(car, args);
        System.out.println(method.getName()+"方法被執(zhí)行后。。。");
        long after = System.currentTimeMillis();
        System.out.println("耗時:" + (after - before));
        return null;
    }
}

要想搞懂JDK動態(tài)代理必須查看動態(tài)生成代理類,通過設(shè)置JDK動態(tài)代理生成的類是否保存的一個屬性將生成的代理類保存下來,通過在程序啟動前加上: 

System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles",true);

代理類的調(diào)用過程

  • 生成的代理類
public final class $Proxy0 extends Proxy implements Mobile {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(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 void move() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

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

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.shaoby.basic.proxy.JdkProxy.Mobile").getMethod("move");
            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());
        }
    }
}
  • 可以看到代理類繼承了Proxy,并實現(xiàn)了我們定義的Mobile接口
  • 構(gòu)造方法傳入InvocationHandler類型,并賦值給父類Proxy中的h參數(shù),可以猜測這個就在獲取代理類時候的第三個參數(shù);
  • 代理類中通過反射生成了四個方法,除了Object中的equals、toString、hashCode外另外一個就是我們要代理的方法move;
  • 在move方法中調(diào)用了 super.h.invoke(this, m3, (Object[])null),即MyInvocationHandler中的invoke方法。
  • 因此在調(diào)用代理類的invoke方法時,調(diào)用的就是MyInvocationHandler中的invoke方法。

Method類

這個類是一個方法的類,說白了就是方法的模板,在這個類中有一個方法invoke,傳入兩個參數(shù),一個是調(diào)用方法的對象,另一個是方法的入?yún)?/p>

InvocationHandler中的invoke方法

可以看到在代理類中invoke方法的調(diào)用為 super.h.invoke(this, m3, (Object[])null);

MyInvocationHandler中的invoke方法:

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long before = System.currentTimeMillis();
        System.out.println(method.getName()+"方法被執(zhí)行前。。。");
        Object invoke = method.invoke(car, args);
        System.out.println(method.getName()+"方法被執(zhí)行后。。。");
        long after = System.currentTimeMillis();
        System.out.println("耗時:" + (after - before));
        return null;
    }

參數(shù):

  • 第一個參數(shù):this,即代理類本身
  • 第二個參數(shù):m3,即move方法
  • 第三個參數(shù):第二個方法的入?yún)?/li>

返回值:

這個返回值其實是被代理方法的返回值,如果沒有返回值就返回null;

這里最重要的是method.invoke(car, args),就是用car對象調(diào)用move方法。

ASM

在上述分析中,動態(tài)代理類的構(gòu)造方法中參數(shù)是我們猜測的。其實這里是用ASM實現(xiàn)的。在生成代理類時調(diào)用的Proxy.newInstance()傳入了InvocationHandler接口的實現(xiàn)類,Proxy.neInstance()會動態(tài)的生成代理類,并產(chǎn)生一個代理對象。這里后續(xù)再研究。

ASM是Java字節(jié)碼操控框架,它能夠以二進(jìn)制形式修改已有類,或動態(tài)生成類,ASM 可以直接產(chǎn)生二進(jìn)制 class 文件,也可以在類被加載入 Java 虛擬機(jī)之前動態(tài)改變類行為。ASM 從類文件中讀入信息后,能夠改變類行為,分析類信息,甚至能夠根據(jù)用戶要求生成新類。

cglib動態(tài)代理

public class Test {

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Plane.class);
        enhancer.setCallback(new MyMethodInterceptor());
        Plane o = (Plane)enhancer.create();
        o.move();


    }
}
class MyMethodInterceptor implements MethodInterceptor{
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        long before = System.currentTimeMillis();
        System.out.println(method.getName()+"方法被執(zhí)行前。。。");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println(method.getName()+"方法被執(zhí)行后。。。");
        long after = System.currentTimeMillis();
        System.out.println("耗時:" + (after - before));
        return result;
    }
}

cglib動態(tài)代理的原理是:

  • 指定代理類的父類
  • 生成代理類
  • 調(diào)用代理類父類的method

總結(jié)

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

相關(guān)文章

  • 詳細(xì)介紹Java函數(shù)式接口

    詳細(xì)介紹Java函數(shù)式接口

    函數(shù)式接口在Java中是指有且僅有一個抽象方法的接口。當(dāng)然接口中可以包含其他的方法默認(rèn)、靜態(tài)、私有,具體內(nèi)容請參考下面文章內(nèi)容
    2021-09-09
  • idea中創(chuàng)建多module的maven工程的方法

    idea中創(chuàng)建多module的maven工程的方法

    這篇文章主要介紹了idea中創(chuàng)建多module的maven工程的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-10-10
  • 關(guān)于Java中Json的各種處理

    關(guān)于Java中Json的各種處理

    這篇文章主要介紹了關(guān)于Java中Json的各種處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • java實現(xiàn)折半排序算法

    java實現(xiàn)折半排序算法

    折半插入排序法,又稱二分插入排序法,是直接插入排序法的改良版,也需要執(zhí)行i-1趟插入,不同之處在于,第i趟插入,先找出第i+1個元素應(yīng)該插入的的位置,假定前i個數(shù)據(jù)是已經(jīng)處于有序狀態(tài)。
    2015-04-04
  • Windows Zookeeper安裝過程及啟動圖解

    Windows Zookeeper安裝過程及啟動圖解

    這篇文章主要介紹了Windows Zookeeper安裝過程及啟動圖解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-12-12
  • 淺談異常結(jié)構(gòu)圖、編譯期異常和運行期異常的區(qū)別

    淺談異常結(jié)構(gòu)圖、編譯期異常和運行期異常的區(qū)別

    下面小編就為大家?guī)硪黄獪\談異常結(jié)構(gòu)圖、編譯期異常和運行期異常的區(qū)別。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-09-09
  • 你知道Spring如何解決所有循環(huán)依賴的嗎

    你知道Spring如何解決所有循環(huán)依賴的嗎

    這篇文章主要來和大家聊聊Spring?能解決所有循環(huán)依賴嗎,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Spring有一定的幫助,需要的小伙伴可以參考一下
    2023-07-07
  • 解析Spring?漏洞及其修復(fù)方案

    解析Spring?漏洞及其修復(fù)方案

    官宣了最近網(wǎng)傳的Spring漏洞。攻擊者利用該漏洞,可在未授權(quán)的情況下遠(yuǎn)程執(zhí)行命令,今天通過本文給大家普及下漏洞分析影響范圍及解決方案,感興趣的朋友跟隨小編一起看看吧
    2022-04-04
  • maven配置阿里倉庫的方法步驟

    maven配置阿里倉庫的方法步驟

    這篇文章主要介紹了maven配置阿里倉庫的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • 一篇文章教你如何用多種迭代寫法實現(xiàn)二叉樹遍歷

    一篇文章教你如何用多種迭代寫法實現(xiàn)二叉樹遍歷

    這篇文章主要介紹了C語言實現(xiàn)二叉樹遍歷的迭代算法,包括二叉樹的中序遍歷、先序遍歷及后序遍歷等,是非常經(jīng)典的算法,需要的朋友可以參考下
    2021-08-08

最新評論

聂拉木县| 广汉市| 尼玛县| 静宁县| 西城区| 蓬安县| 铜陵市| 玛沁县| 昌都县| 朝阳县| 柳州市| 雅安市| 安平县| 安仁县| 盘锦市| 溧阳市| 澄迈县| 巴马| 富裕县| 黔江区| 杂多县| 西乌珠穆沁旗| 汪清县| 南靖县| 大冶市| 山东| 额济纳旗| 蕲春县| 出国| 山西省| 开原市| 潞城市| 永昌县| 泗洪县| 湛江市| 贵南县| 姚安县| 宜宾市| 乐昌市| 乳山市| 湖北省|