欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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é)碼操控框架,它能夠以二進制形式修改已有類,或動態(tài)生成類,ASM 可以直接產(chǎn)生二進制 class 文件,也可以在類被加載入 Java 虛擬機之前動態(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)文章

  • MyBatisPlus3如何向數(shù)據(jù)庫中存入List

    MyBatisPlus3如何向數(shù)據(jù)庫中存入List

    本文主要介紹了Mybatis Plus的類型處理器的使用,通過User.java和UserMapper.xml示例進行詳細的解析,并提供了JSON解析器的使用方法,希望通過這篇文章,可以幫助大家更好的理解和掌握Mybatis Plus的類型處理器
    2024-10-10
  • 詳解在springmvc中解決FastJson循環(huán)引用的問題

    詳解在springmvc中解決FastJson循環(huán)引用的問題

    本篇文章主要介紹了在springmvc中解決FastJson循環(huán)引用的問題,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-01-01
  • Spring Boot 使用Druid詳解

    Spring Boot 使用Druid詳解

    本篇文章主要介紹了Spring Boot 使用Druid配置詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • JAVA?POI設(shè)置EXCEL單元格格式用法舉例

    JAVA?POI設(shè)置EXCEL單元格格式用法舉例

    這篇文章主要給大家介紹了關(guān)于JAVA?POI設(shè)置EXCEL單元格格式用法的相關(guān)資料,POI中可能會用到一些需要設(shè)置EXCEL單元格格式的操作,需要的朋友可以參考下
    2023-08-08
  • java socket 詳細介紹

    java socket 詳細介紹

    本篇文章小編為大家介紹,java socket 詳細介紹。需要的朋友參考下
    2013-04-04
  • IntelliJIDEA中實現(xiàn)SpringBoot多實例運行的兩種方式

    IntelliJIDEA中實現(xiàn)SpringBoot多實例運行的兩種方式

    在微服務(wù)開發(fā)中,經(jīng)常需要同時啟動多個服務(wù)實例進行測試或模擬集群環(huán)境,?IntelliJ?IDEA?作為Java開發(fā)者常用工具,提供了靈活的多實例啟動支持,本文將詳細介紹如何通過修改配置?和批量啟動?兩種方式實現(xiàn)SpringBoot多實例運行,并解決常見問題,需要的朋友可以參考下
    2025-03-03
  • java 動態(tài)生成SQL的實例講解

    java 動態(tài)生成SQL的實例講解

    下面小編就為大家?guī)硪黄猨ava 動態(tài)生成SQL的實例講解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • spring mvc4中相關(guān)注解的詳細講解教程

    spring mvc4中相關(guān)注解的詳細講解教程

    這篇文章主要給大家介紹了關(guān)于spring mvc4中相關(guān)注解的相關(guān)資料,其中詳細介紹了關(guān)于@Controller、@RequestMapping、@RathVariable、@RequestParam及@RequestBody等等注解的相關(guān)內(nèi)容,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-06-06
  • 使用IntelliJ IDEA查看類的繼承關(guān)系圖形(圖文詳解)

    使用IntelliJ IDEA查看類的繼承關(guān)系圖形(圖文詳解)

    這篇文章主要介紹了使用IntelliJ IDEA查看類的繼承關(guān)系圖形,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的工作或?qū)W習(xí)具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03
  • Java spring定時任務(wù)詳解

    Java spring定時任務(wù)詳解

    這篇文章主要為大家詳細介紹了Spring定時任務(wù),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-10-10

最新評論