溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

大數(shù)據(jù)開(kāi)發(fā)中動(dòng)態(tài)代理是什么

發(fā)布時(shí)間:2021-12-27 10:46:10 來(lái)源:億速云 閱讀:127 作者:小新 欄目:大數(shù)據(jù)

這篇文章主要介紹了大數(shù)據(jù)開(kāi)發(fā)中動(dòng)態(tài)代理是什么,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

一、動(dòng)態(tài)代理的意義

 首先明白一點(diǎn),動(dòng)態(tài)代理就是用來(lái)生成代理對(duì)象的。我們知道傳統(tǒng)的代理模式,通常是先定義一個(gè)代理類,該代理類需要持有目標(biāo)對(duì)象(也有叫被代理對(duì)象,我覺(jué)得都行吧)。假設(shè)我們有1000個(gè)不同的目標(biāo)對(duì)象(這1000個(gè)對(duì)象不是同一個(gè)類),那么我們需要預(yù)先定義1000個(gè)代理類,這是我們不能容忍的。于是乎,動(dòng)態(tài)代理就出現(xiàn)了,它本質(zhì)上是生成一個(gè)外表上和目標(biāo)對(duì)象一樣的代理對(duì)象,然后當(dāng)我們調(diào)用代理對(duì)象的方法的時(shí)候,實(shí)際上它在他的方法里面去調(diào)用了目標(biāo)對(duì)象對(duì)應(yīng)的同名方法。

二、動(dòng)態(tài)代理設(shè)計(jì)的核心思想

其實(shí)不要把這些設(shè)計(jì)想得多么高尚,假如我是動(dòng)態(tài)代理設(shè)計(jì)的作者,由動(dòng)態(tài)代理的意義部分我們知道,我們就是要想盡一切辦法,通過(guò)目標(biāo)對(duì)象生成代理對(duì)象,然后讓代理對(duì)象的方法調(diào)用作用到目標(biāo)對(duì)象的方法調(diào)用。沒(méi)錯(cuò)動(dòng)態(tài)代理的核心思想就是這么簡(jiǎn)單。比如目標(biāo)類為Person,Person有一個(gè)方法叫做purchase(),此方法用于購(gòu)物。我們期望purchase()方法有代理類去做處理,比如在購(gòu)物前記錄下購(gòu)買了哪些東西。我們知道在使用一個(gè)類之前,是需要?jiǎng)?chuàng)建一個(gè)對(duì)象的,我們就在創(chuàng)建的地方動(dòng)手腳。所以你看到了JDK動(dòng)態(tài)Proxy.newInstance()的方式,也領(lǐng)略過(guò)Spring的Enhancer.create()。個(gè)人比較喜歡cglib的優(yōu)雅、干凈、利落。吐槽一下JDK的InvocationHandler像極了惡心的中間商。下面是JDK動(dòng)態(tài)代理UML示意圖

大數(shù)據(jù)開(kāi)發(fā)中動(dòng)態(tài)代理是什么

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

1,原理

在了解動(dòng)態(tài)代理之前,我們需要了解Java字節(jié)碼。如果不熟悉Java字節(jié)碼,你可以理解為通過(guò)代碼動(dòng)態(tài)生成一個(gè).java文件,然后將其編譯為class文件加載到內(nèi)存中。接下來(lái)JDK中的動(dòng)態(tài)代理要做的事情就是怎么去生成一個(gè)ProxyPerson字節(jié)碼文件。其實(shí)它就是在生成字節(jié)碼的時(shí)候,持有了InvocationHandler對(duì)象,然后去實(shí)現(xiàn)了ProxyPerson對(duì)應(yīng)的接口。在該接口的所有實(shí)現(xiàn)方法中,只做了一件事情就是調(diào)用invocationHandler.invoke()方法。從代碼層面來(lái)看如下所示:

public class ProxyPerson implements Purchase{

static{   Method method;// 接口的方法   Object[] args;// 接口參數(shù)  }
InvocationHandler handler;public ProxyPerson(InvocationHandler handler){    this.handler = handler;}

@overrdepublic purchage(){   this,handler.invoke(this,method,args);  }
}

 那么上面這段代碼是在什么時(shí)候生成的呢? 

Proxy.newProxyInstance()

在我們調(diào)用JDK上面的這個(gè)方法的時(shí)候,底層就會(huì)去生成一個(gè)ProxyPerson字節(jié)碼。知道了原理我們來(lái)解答一下JDK動(dòng)態(tài)代理為何只能基于接口代理而不能基于類呢?

1),受限于字節(jié)碼的生成方式,JDK本身就是基于InvocationHandler去做的代理中轉(zhuǎn)。我們看到代理對(duì)象的方法調(diào)用于目標(biāo)對(duì)象的調(diào)用沒(méi)有半毛球關(guān)系,調(diào)用目標(biāo)對(duì)象是我們自己在invoke方法里面完成的。

2),受限于同名的方法只能被向上轉(zhuǎn)型成功的對(duì)象調(diào)用。比如有兩個(gè)類Boy與Girl,他們都實(shí)現(xiàn)了接口Purchase,如果我們先獲取到Girl的purchase()方法method,我們通過(guò)method.invoke(new Boy())這樣必定會(huì)報(bào)錯(cuò)。但是如果我們獲取到Purchase接口purchase()方法method,我們通過(guò)method.invoke(new Boy())這樣是ok的,因?yàn)閚ew Boy()可以向上轉(zhuǎn)型為Purchase。

2,應(yīng)用

比如無(wú)論是傳統(tǒng)的MVC模型還是DDD模型,都離不開(kāi)Service。我們知道Service方法使用@Transactional是可以開(kāi)啟事務(wù)控制的。那么這種注解式事務(wù)是如何實(shí)現(xiàn)的呢?其實(shí)在工程啟動(dòng)的時(shí)候,我們就會(huì)有一個(gè)Bean的后置處理器去檢查所有Bean一旦發(fā)現(xiàn)Bean的方法上有事務(wù)注解,他就通過(guò)Proxy.newInstance()去創(chuàng)建一個(gè)代理對(duì)象,將代理對(duì)象進(jìn)行返回注入,而拋棄原本應(yīng)該注入到容器的對(duì)象。所以我們看起來(lái)通過(guò)容器拿到的Service其實(shí)已經(jīng)是代理對(duì)象了。在調(diào)用目標(biāo)對(duì)象前,開(kāi)啟編程式事務(wù)即可。

四、cglib動(dòng)態(tài)代理

 有了上面的知識(shí),我們要有對(duì)于cglib而言只是在生成字節(jié)碼上面動(dòng)手腳的覺(jué)悟。下面直觀感受與一下生成過(guò)程

public static void main(final String[] args) {        Enhancer enhancer = new Enhancer();       enhancer.setSuperclass(Boy.class);       enhancer.setCallback(new MethodInterceptor(){            @Override           public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {               System.out.println("proxy method "+ method.getName());               if(method.getAnnotation(Transactional.class)!=null){                   System.out.println(method.getName()+"發(fā)現(xiàn)注解");               }               return methodProxy.invokeSuper(o,args);           }       });       Boy proxy = (Boy) enhancer.create();       proxy.test();     }    public static class Boy{       public void run(){           System.out.println("run...");       }       @Transactional       public void walk(){           System.out.println("walk...");       }       @Transactional       public void test(){           System.out.println("test...");           walk();       }   }

可以看到cglib是基于繼承的方式進(jìn)行字節(jié)碼動(dòng)態(tài)生成。 它在子類的實(shí)現(xiàn)中,只是調(diào)用了注入的methodIntercptor.interceptor()方法。 具體字節(jié)碼實(shí)現(xiàn)細(xì)節(jié),這里不在深究。 我們?cè)谶@里探討一下,為什么cglib可以使同一個(gè)service方法中的其他帶有事務(wù)注解的事務(wù)生效?因?yàn)榛诶^承的動(dòng)態(tài)代理,本質(zhì)發(fā)起上調(diào)用的代理對(duì)象可以向上轉(zhuǎn)型為原本的目標(biāo)對(duì)象,所以它可以直接通過(guò)代理對(duì)象去調(diào)目標(biāo)對(duì)象方法。  

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“大數(shù)據(jù)開(kāi)發(fā)中動(dòng)態(tài)代理是什么”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI