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

Java中的動態(tài)代理原理及實現(xiàn)

 更新時間:2023年12月20日 09:30:03   作者:Brain_L  
這篇文章主要介紹了Java中的動態(tài)代理原理及實現(xiàn),動態(tài)是相對于靜態(tài)而言,何為靜態(tài),即編碼時手動編寫代理類、委托類,而動態(tài)呢,是不編寫具體實現(xiàn)類,等到使用時,動態(tài)創(chuàng)建一個來實現(xiàn)代理的目的,需要的朋友可以參考下

前言

動態(tài)是相對于靜態(tài)而言,何為靜態(tài),即編碼時手動編寫代理類、委托類。而動態(tài)呢,是不編寫具體實現(xiàn)類,等到使用時,動態(tài)創(chuàng)建一個來實現(xiàn)代理的目的。

為什么有了靜態(tài)代理還需要動態(tài)代理呢?靜態(tài)代理畢竟是你手動編碼的,如果需要對很多個方法進行一些公共處理(比如耗時,日志等),你需要在每個方法處修改代碼,而且邏輯上都是相通的,為什么不能抽取出來呢。如果使用動態(tài)代理的話,你只需要指定規(guī)則,那么動態(tài)代理就可以根據(jù)你指定的規(guī)則進行處理。

本文主要研究動態(tài)代理的兩種實現(xiàn)方式:JDK動態(tài)代理和CGLib動態(tài)代理

一、JDK動態(tài)代理

JDK動態(tài)代理的核心是JDK提供的Proxy類和Invocation接口,基于接口。

看個例子

1、公共接口

public interface Hello {
    void sayHi(String name);
}

2、委托類

@Slf4j
public class HelloImpl implements Hello {
    @Override
    public void sayHi(String name) {
        log.info("hello, {}", name);
    }
}

3、實現(xiàn)InvocationHandler接口

@Slf4j
public class HelloInvocationHandler<T> implements InvocationHandler {
    private T target;
    public HelloInvocationHandler(T target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("執(zhí)行了{}方法", method.getName());
        method.invoke(target, args);
        return null;
    }
}

為什么實現(xiàn)這個接口呢,主要是實現(xiàn)其invoke方法,該方法有三個參數(shù)

proxy——動態(tài)代理實例

method——被調(diào)用的方法

args——方法入?yún)ⅲ绻菬o參方法,則為null

在invoke里,我們就可以對方法進行一些特殊處理,這里只做了一個簡單的演示,在執(zhí)行委托類的方法之前,打印一行日志。實際可以在方法前、方法后、方法異常等等場景進行想要的處理。

4、創(chuàng)建代理類

@Slf4j
public class ProxyTest {
    public static void main(String[] args) {
        Hello hello = new HelloImpl();
        InvocationHandler handler = new HelloInvocationHandler<>(hello);
        Hello helloProxy = (Hello) Proxy.newProxyInstance(Hello.class.getClassLoader(), new Class<?>[]{Hello.class}, handler);
        helloProxy.sayHi("proxy");
    }
}
//輸出
執(zhí)行了sayHi方法
hello, proxy

根據(jù)3創(chuàng)建一個調(diào)用處理器handler,通過Proxy的newProxyInstance方法生成代理類的實例。

入?yún)⒎謩e為:classLoader,要代理的接口列表,調(diào)用分發(fā)器handler。

通過該代理實例調(diào)用方法,將會回調(diào)hanlder中的invoke,從而達到代理的目的。

上述例子是直接調(diào)用newProxyInstance來生成代理實例,還有一種方法是先生成代理類,然后再構造代理實例。

@Slf4j
public class ProxyTest {
    public static void main(String[] args) throws Exception {
        Hello hello = new HelloImpl();
        InvocationHandler handler = new HelloInvocationHandler<>(hello);
//        Hello helloProxy = (Hello) Proxy.newProxyInstance(Hello.class.getClassLoader(), new Class<?>[]{Hello.class}, handler);
//        helloProxy.sayHi("proxy");
        Class<?> proxyClass = Proxy.getProxyClass(Hello.class.getClassLoader(), Hello.class);
        log.info("name:{}", proxyClass.getName());
        Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
        Hello helloInstance = (Hello) constructor.newInstance(handler);
        helloInstance.sayHi("proxy");
    }
}

//輸出
name:com.sun.proxy.$Proxy0
執(zhí)行了sayHi方法
hello, proxy

生成的代理類名稱為$Proxy0,0為Proxy遞增生成的編號,如果有多個代理類,則名稱從$Proxy1依次類推。將生成的代理類proxyClass保存下來(默認保存到內(nèi)存中,并不會保存成文件,此處只是為了研究),命名為Hello$Proxy0.class,打開看下

public final class Hello$Proxy0 extends Proxy implements Hello {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    public Hello$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 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 void sayHi(String var1) throws  {
        try {
            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);
        }
    }
    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.brain.demo.aop.Hello").getMethod("sayHi", 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());
        }
    }
}

代理類繼承Proxy,實現(xiàn)了Hello接口。static代碼塊中,通過反射拿到接口中的方法(本例中為Hello中的sayHi方法),Object中的equals、toString、hashCode方法。

對這些方法分別進行代理,具體表現(xiàn)為通過代理實例調(diào)用方法時,回調(diào)InvocationHandler實例中的invoke方法,即3中所述。

至此,JDK動態(tài)代理的流程就清楚了。Proxy生成代理類,持有InvocationHandler實例,而InvocationHandler實例又持有委托類實例。通過代理類實例調(diào)用接口方法,由InvocationHandler實例攔截,進行相應處理后再調(diào)用真正的實現(xiàn)方法。

二、CGLib動態(tài)代理

CGLib基于繼承,通過繼承代理類覆蓋其中的方法來實現(xiàn)代理的功能。

1、委托類

@Slf4j
public class Teacher {
    public void sayHi() {
        log.info("大家好");
    }
}

2、方法攔截器

@Slf4j
public class CglibMethodInterceptor implements MethodInterceptor {
    public Object CglibProxyGeneratory(Class target) {
        // 創(chuàng)建加強器,用來創(chuàng)建動態(tài)代理類
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        log.info("調(diào)用了方法:{}", method.getName());
        methodProxy.invokeSuper(o, objects);
        return null;
    }
}

3、生成代理類實例

Teacher teacher = (Teacher) new CglibMethodInterceptor().CglibProxyGeneratory(Teacher.class);
        teacher.sayHi();

//輸出
調(diào)用了方法:sayHi
大家好

將生成的委托類保存下來,發(fā)現(xiàn)會有三個class文件生成。

Teacher$$FastClassByCGLIB$$4e4ecf50(委托類fastclass)
Teacher$$EnhancerByCGLIB$$332f7724(代理類)
Teacher$$EnhancerByCGLIB$$332f7724$$FastClassByCGLIB$$3b18b46c(代理類fastclass)

看下生成的代理類

public class Teacher$$EnhancerByCGLIB$$332f7724 extends Teacher implements Factory {
    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$sayHi$0$Method;
    private static final MethodProxy CGLIB$sayHi$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$finalize$1$Method;
    private static final MethodProxy CGLIB$finalize$1$Proxy;
    private static final Method CGLIB$equals$2$Method;
    private static final MethodProxy CGLIB$equals$2$Proxy;
    private static final Method CGLIB$toString$3$Method;
    private static final MethodProxy CGLIB$toString$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;
    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.brain.demo.aop.Teacher$$EnhancerByCGLIB$$332f7724");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = var10000[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
        CGLIB$equals$2$Method = var10000[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        CGLIB$toString$3$Method = var10000[2];
        CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        CGLIB$hashCode$4$Method = var10000[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
        CGLIB$clone$5$Method = var10000[4];
        CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        CGLIB$sayHi$0$Method = ReflectUtils.findMethods(new String[]{"sayHi", "()V"}, (var1 = Class.forName("com.brain.demo.aop.Teacher")).getDeclaredMethods())[0];
        CGLIB$sayHi$0$Proxy = MethodProxy.create(var1, var0, "()V", "sayHi", "CGLIB$sayHi$0");
    }
    final void CGLIB$sayHi$0() {
        super.sayHi();
    }
    public final void sayHi() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }
        if (var10000 != null) {
            var10000.intercept(this, CGLIB$sayHi$0$Method, CGLIB$emptyArgs, CGLIB$sayHi$0$Proxy);
        } else {
            super.sayHi();
        }
    }
    public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
        String var10000 = var0.toString();
        switch(var10000.hashCode()) {
        case -2012941911:
            if (var10000.equals("sayHi()V")) {
                return CGLIB$sayHi$0$Proxy;
            }
            break;
        case -1574182249:
            if (var10000.equals("finalize()V")) {
                return CGLIB$finalize$1$Proxy;
            }
            break;
        case -508378822:
            if (var10000.equals("clone()Ljava/lang/Object;")) {
                return CGLIB$clone$5$Proxy;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return CGLIB$equals$2$Proxy;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return CGLIB$toString$3$Proxy;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return CGLIB$hashCode$4$Proxy;
            }
        }
        return null;
    }
    public Teacher$$EnhancerByCGLIB$$332f7724() {
        CGLIB$BIND_CALLBACKS(this);
    }
    private static final void CGLIB$BIND_CALLBACKS(Object var0) {
        Teacher$$EnhancerByCGLIB$$332f7724 var1 = (Teacher$$EnhancerByCGLIB$$332f7724)var0;
        if (!var1.CGLIB$BOUND) {
            var1.CGLIB$BOUND = true;
            Object var10000 = CGLIB$THREAD_CALLBACKS.get();
            if (var10000 == null) {
                var10000 = CGLIB$STATIC_CALLBACKS;
                if (var10000 == null) {
                    return;
                }
            }
            var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
        }
    }
    static {
        CGLIB$STATICHOOK1();
    }
}

初始化時,獲得Object的finalize、equals、toString、hashCode、clone以及代理類Teacher的sayHi的方法和方法代理。

看下代理類中的sayHi方法,首先獲取MethodInterceptor實例,如果創(chuàng)建時為空,則調(diào)用super.sayHi(),即代理類中的方法;如果不為空,進行方法攔截,調(diào)用interceptor即2中所示。

類中還有一個sayHi方法,即CGLIB$sayHi$0,里面就是單純的調(diào)用Teacher中的sayHi。那么這個方法是干嘛的呢?看下初始化的最后有這一句

CGLIB$sayHi$0$Proxy = MethodProxy.create(var1, var0, "()V", "sayHi", "CGLIB$sayHi$0");

它創(chuàng)建了一個方法代理,并指明了Teacher的sayHi的代理方法是CGLIB$sayHi$0。調(diào)用sayHi并進行方法攔截時,會將CGLIB$sayHi$0$Proxy做為入?yún)魅搿T?的interceptor中,調(diào)用methodProxy.invokeSuper完成方法調(diào)用。

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }
//DCL單例模式
private void init() {
        if (this.fastClassInfo == null) {
            synchronized(this.initLock) {
                if (this.fastClassInfo == null) {
                    MethodProxy.CreateInfo ci = this.createInfo;
                    MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(this.sig1);
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }
    }

init方法通過DCL加載fastClassInfo,其中fci.f1為Teacher FastClass,fci.f2為Teacher Enhancer FastClass,也即前面提到的生成三個class文件的另外兩個。

getIndex是根據(jù)方法簽名的hashCode給出的索引值。init完成之后,通過調(diào)用Teacher Enhancer FastClass中的invoke方法,將剛才計算的索引值和入?yún)魅?,這里根據(jù)索引值查到要調(diào)用代理類的CGLIB$sayHi$0,最終調(diào)用了Teacher當中的sayHi。這一套流程下來才算完成。

MethodInterceptor中的invoke和invokeSuper流程上一致,只是它調(diào)用的是Teacher FastClass中的invoke方法,然后調(diào)用sayHi。整個過程比JDK動態(tài)代理要繞,畫個圖總結下

cglib是基于繼承的,所以委托類中的static、private、final方法因為無法繼承所以無法代理

三、總結

JDK動態(tài)代理基于接口,如果委托類沒有實現(xiàn)接口或者有自定義方法,則無法完成代理。CGLib基于繼承,不受接口的限制,但是不能代理static、private、final方法。

JDK動態(tài)代理是通過反射完成方法調(diào)用,比較消耗性能。CGLib通過建立方法索引,不會有反射帶來的性能問題。

JDK動態(tài)代理只會生成一個代理類。CGLib會生成三個代理類。

兩者都可以用來實現(xiàn)AOP。Spring中兩者均有使用。

到此這篇關于Java中的動態(tài)代理原理及實現(xiàn)的文章就介紹到這了,更多相關Java動態(tài)代理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • JDBC如何通過SSL方式連接MySQL

    JDBC如何通過SSL方式連接MySQL

    文章介紹了如何配置MySQL以支持SSL連接,并通過JDBC進行安全連接,主要內(nèi)容包括查看MySQL SSL支持、創(chuàng)建SSL連接用戶、配置用戶是否強制使用SSL、JDBC配置導入證書以及使用Go編寫一個簡單的HTTP文件服務器來提供SSL證書
    2025-02-02
  • springCloud gateWay 統(tǒng)一鑒權的實現(xiàn)代碼

    springCloud gateWay 統(tǒng)一鑒權的實現(xiàn)代碼

    這篇文章主要介紹了springCloud gateWay 統(tǒng)一鑒權的實現(xiàn)代碼,統(tǒng)一鑒權包括鑒權邏輯和代碼實現(xiàn),本文給大家介紹的非常詳細,需要的朋友可以參考下
    2022-02-02
  • Netty分布式ByteBuf使用SocketChannel讀取數(shù)據(jù)過程剖析

    Netty分布式ByteBuf使用SocketChannel讀取數(shù)據(jù)過程剖析

    這篇文章主要為大家介紹了Netty源碼分析ByteBuf使用SocketChannel讀取數(shù)據(jù)過程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-03-03
  • Java多線程鎖機制相關原理實例解析

    Java多線程鎖機制相關原理實例解析

    這篇文章主要介紹了Java多線程鎖機制相關原理實例解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-08-08
  • Java ForkJoin框架的原理及用法

    Java ForkJoin框架的原理及用法

    這篇文章主要介紹了Java ForkJoin框架的原理及用法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-10-10
  • 2022最新Java泛型詳解(360度無死角介紹)

    2022最新Java泛型詳解(360度無死角介紹)

    Java泛型(generics)是JDK5中引入的一個新特性,泛型提供了 編譯時類型安全監(jiān)測機制,該機制允許我們在編譯時檢測到非法的類型數(shù)據(jù)結構,這篇文章主要介紹了java泛型的基本概念及使用詳解,感興趣的朋友跟隨小編一起看看吧
    2022-10-10
  • Java實現(xiàn)Floyd算法求最短路徑

    Java實現(xiàn)Floyd算法求最短路徑

    這篇文章主要為大家詳細介紹了Java實現(xiàn)Floyd算法求最短路徑,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • ZooKeeper入門教程一簡介與核心概念

    ZooKeeper入門教程一簡介與核心概念

    本文是ZooKeeper入門系列教程,涵蓋ZooKeeper核心內(nèi)容,通過實例和大量圖表,結合實戰(zhàn),幫助學習者理解和運用,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-01-01
  • Java 中的io模型詳解

    Java 中的io模型詳解

    這篇文章主要介紹了Java 中io模型的相關資料,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下
    2021-04-04
  • java圖搜索算法之DFS與BFS詳解

    java圖搜索算法之DFS與BFS詳解

    這篇文章主要為大家介紹了java數(shù)據(jù)結構中可以秒殺一切圖算法的DFS與BFS作用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-11-11

最新評論