亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java中的動(dòng)態(tài)代理原理及實(shí)現(xiàn)

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

前言

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

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

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

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

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

看個(gè)例子

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、實(shí)現(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í)行了{(lán)}方法", method.getName());
        method.invoke(target, args);
        return null;
    }
}

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

proxy——動(dòng)態(tài)代理實(shí)例

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

args——方法入?yún)?,如果是無(wú)參方法,則為null

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

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)建一個(gè)調(diào)用處理器handler,通過(guò)Proxy的newProxyInstance方法生成代理類的實(shí)例。

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

通過(guò)該代理實(shí)例調(diào)用方法,將會(huì)回調(diào)hanlder中的invoke,從而達(dá)到代理的目的。

上述例子是直接調(diào)用newProxyInstance來(lái)生成代理實(shí)例,還有一種方法是先生成代理類,然后再構(gòu)造代理實(shí)例

@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遞增生成的編號(hào),如果有多個(gè)代理類,則名稱從$Proxy1依次類推。將生成的代理類proxyClass保存下來(lái)(默認(rèn)保存到內(nèi)存中,并不會(huì)保存成文件,此處只是為了研究),命名為Hello$Proxy0.class,打開(kāi)看下

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,實(shí)現(xiàn)了Hello接口。static代碼塊中,通過(guò)反射拿到接口中的方法(本例中為Hello中的sayHi方法),Object中的equals、toString、hashCode方法。

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

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

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

CGLib基于繼承,通過(guò)繼承代理類覆蓋其中的方法來(lái)實(shí)現(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)建加強(qiáng)器,用來(lái)創(chuàng)建動(dò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、生成代理類實(shí)例

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

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

將生成的委托類保存下來(lái),發(fā)現(xiàn)會(huì)有三個(gè)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();
    }
}

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

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

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

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

它創(chuàng)建了一個(gè)方法代理,并指明了Teacher的sayHi的代理方法是CGLIB$sayHi$0。調(diào)用sayHi并進(jìn)行方法攔截時(shí),會(huì)將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方法通過(guò)DCL加載fastClassInfo,其中fci.f1為T(mén)eacher FastClass,fci.f2為T(mén)eacher Enhancer FastClass,也即前面提到的生成三個(gè)class文件的另外兩個(gè)。

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

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

cglib是基于繼承的,所以委托類中的static、private、final方法因?yàn)闊o(wú)法繼承所以無(wú)法代理

三、總結(jié)

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

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

JDK動(dòng)態(tài)代理只會(huì)生成一個(gè)代理類。CGLib會(huì)生成三個(gè)代理類。

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

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

相關(guān)文章

  • JDBC如何通過(guò)SSL方式連接MySQL

    JDBC如何通過(guò)SSL方式連接MySQL

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

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

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

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

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

    Java多線程鎖機(jī)制相關(guān)原理實(shí)例解析

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

    Java ForkJoin框架的原理及用法

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

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

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

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

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

    ZooKeeper入門(mén)教程一簡(jiǎn)介與核心概念

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

    Java 中的io模型詳解

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

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

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

最新評(píng)論