摘要:類所實(shí)現(xiàn)的方法包裝了對被代理對象的反射調(diào)用,后文中的動態(tài)代理類正是調(diào)用此方法來調(diào)用被代理對象的方法。
前言
java的設(shè)計(jì)模式中有一項(xiàng)設(shè)計(jì)模式叫做代理模式,所謂代理模式,就是通過代理方來操作目標(biāo)對象,而不是自己直接調(diào)用。代理又分為靜態(tài)代理和動態(tài)代理,靜態(tài)代理就是針對每個被代理對象寫一個代理類,操作不夠優(yōu)雅;動態(tài)代理,可以根據(jù)接口動態(tài)的生成代理類,這動態(tài)生成的類不需要自己書寫,jdk幫你完成了。無論是動態(tài)代理還是靜態(tài)代理,最終都會產(chǎn)生一個代理類(class文件),里面都含有對被代理對象的封裝,只是誕生的途徑不一樣。下面我在代碼層面詳細(xì)介紹一下這兩種代理的實(shí)現(xiàn)和原理。
本文來自于我的博客網(wǎng)站http://51think.net,歡迎來訪。
一、靜態(tài)代理1、創(chuàng)建手機(jī)接口 ,擁有打電話的行為
public interface MobilePhone { //打電話給jack void callJack(); }
2、創(chuàng)建實(shí)現(xiàn)類安卓手機(jī),實(shí)現(xiàn)此接口
public class AndroidMobilePhone implements MobilePhone{ private String name; private String age; public AndroidMobilePhone(String name, String age) { this.name = name; this.age = age; } //打電話給jack @Override public void callJack(){ System.out.println(" hey boy! name="+name+",age="+age); } }
3、創(chuàng)建靜態(tài)代理類,實(shí)現(xiàn)此接口
public class AndroidMobileStaticProxyPhone implements MobilePhone{ private MobilePhone amp; public AndroidMobileStaticProxyPhone(MobilePhone amp) { this.amp = amp; } //打電話給jack @Override public void callJack(){ System.out.println("--靜態(tài)代理前置--"); amp.callJack(); System.out.println("--靜態(tài)代理后置--"); } }
從靜態(tài)代理類AndroidMobileStaticProxyPhone 中,我們可以發(fā)現(xiàn),他持有了MobilePhone 類型的對象,一旦將被代理對象傳入,它就可以操作被代理對象了。
4、創(chuàng)建main方法調(diào)用
如果我們不使用代理,調(diào)用是這樣的:
MobilePhone mp=new AndroidMobilePhone("杰克","23"); mp..callJack();
如果使用靜態(tài)代理,調(diào)用變成如下方式:
MobilePhone mp=new AndroidMobilePhone("杰克","23"); MobilePhone staticProxy=new AndroidMobileStaticProxyPhone(mp); staticProxy.callJack();
從上述代碼中,我們可以看出,靜態(tài)代理其實(shí)就是通過一個包裝類來調(diào)用目標(biāo)對象而已。
二、動態(tài)代理1、仍然沿用MobilePhone接口類
2、創(chuàng)建java.lang.reflect.InvocationHandler接口的實(shí)現(xiàn)類MobilePhoneHandler
public class MobilePhoneHandlerimplements InvocationHandler { private T target; public MobilePhoneHandler(T target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //前置處理 System.out.println("--動態(tài)代理前置處理--"); Object obj=method.invoke(target,args); //后置處理 System.out.println("--動態(tài)代理后置處理--"); return obj; } }
關(guān)于InvocationHandler ,源碼注釋如下:
*Each proxy instance has an associated invocation handler. * When a method is invoked on a proxy instance, the method * invocation is encoded and dispatched to the {@code invoke} * method of its invocation handler.
即,每個代理實(shí)例都需要關(guān)聯(lián)一個invocation handler,當(dāng)一個方法被代理實(shí)例調(diào)用時,這個方法會被編碼并發(fā)送到invocation handler中進(jìn)行處理。這里所說的invocation handler即本文中剛剛創(chuàng)建的 MobilePhoneHandler
3、創(chuàng)建main方法調(diào)用
MobilePhone mp=new AndroidMobilePhone("杰克","23"); InvocationHandler handler=new MobilePhoneHandler(mp); MobilePhone mpProxy=(MobilePhone)Proxy.newProxyInstance(MobilePhone.class.getClassLoader(),new Class>[]{MobilePhone.class},handler ); mpProxy.callJack();
輸出如下:
--動態(tài)代理前置處理-- hey boy! name=杰克,age=23 --動態(tài)代理后置處理--
在輸出內(nèi)容的前置處理和后置處理中,我們可以加一些橫向的處理邏輯,這樣就變成了spring 的AOP。
關(guān)注Proxy.newProxyInstance這個方法調(diào)用,同樣是來自于java.lang.reflect包里的類。看一下源碼注釋:
* Returns an instance of a proxy class for the specified interfaces * that dispatches method invocations to the specified invocation * handler. This method is equivalent to: ** Proxy.getProxyClass(loader, interfaces). * getConstructor(new Class[] { InvocationHandler.class }). * newInstance(new Object[] { handler }); **
注釋中表明,這個newProxyInstance方法返回了一個特定接口代理類的實(shí)例,這個代理實(shí)例將方法調(diào)用分配給特定的invocation handler。這個Proxy.newProxyInstance方法等同于如下調(diào)用:
Proxy.getProxyClass(loader, interfaces). getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler });
我們用debug方式跟蹤一下代碼,newProxyInstance方法最終會執(zhí)行到Proxy的內(nèi)部類ProxyClassFactory的apply方法:
long num = nextUniqueNumber.getAndIncrement();這一行使用cas生成一個自增長的序號 。
關(guān)注ProxyGenerator.generateProxyClass 方法:
此方法動態(tài)生成一個class文件,這個class文件就是我們所說的動態(tài)代理類! 我們用代碼的方式將這個class文件寫出來:
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", AndroidMobilePhone.class.getInterfaces()); String path = "E:projectspaceTestincomproxyMobileProxy.class"; try(FileOutputStream fos = new FileOutputStream(path)) { fos.write(classFile); fos.flush(); System.out.println("代理類class文件寫入成功"); } catch (Exception e) { System.out.println("寫文件錯誤"); }
到目錄中找到此class文件:
反編先看一下反編譯的類名和實(shí)現(xiàn)關(guān)系:
從此圖中可以看出,動態(tài)代理類最終還是實(shí)現(xiàn)了我們的MobilePhone接口,即動態(tài)代理類也是MobilePhone接口的一個實(shí)現(xiàn)類,它也實(shí)現(xiàn)了callJack方法。如下:
紅框標(biāo)注this.h.invoke(this, m3, null);中的h正是我們上文中創(chuàng)建的MobilePhoneHandler類的對象。這樣即可完成對被代理對象的調(diào)用。類的調(diào)用關(guān)系如下:
總結(jié)我們再看一下之前main方法中的這一行:
MobilePhone mpProxy=(MobilePhone)Proxy.newProxyInstance(MobilePhone.class.getClassLoader(),new Class>[]{MobilePhone.class},handler );
現(xiàn)在可以得知Proxy.newProxyInstance返回的是動態(tài)生成的代理類$Proxy0的對象,也可以稱作是MobilePhone 接口的一個實(shí)現(xiàn)類的對象。當(dāng)調(diào)用mpProxy.callJack()時,其實(shí)是調(diào)用$Proxy0.callJack(),然后對照剛剛的類調(diào)用關(guān)系圖,即可調(diào)用到被代理對象AndroidMobilePhone實(shí)例的callJack方法,從而實(shí)現(xiàn)了動態(tài)代理。
當(dāng)我們具象的查看某一個動態(tài)代理class反編譯文件時,比如$Proxy0,它內(nèi)部就是采用靜態(tài)代理的方式進(jìn)行包裝。其動態(tài)是體現(xiàn)在,能夠在給定的接口和invocationHandler情況下,動態(tài)生成代理類,如$Proxy0,$Proxy1,$Proxy2等等,不必手動創(chuàng)建,使用起來更靈活。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/74176.html
摘要:,,面向切面編程。,切點(diǎn),切面匹配連接點(diǎn)的點(diǎn),一般與切點(diǎn)表達(dá)式相關(guān),就是切面如何切點(diǎn)。例子中,注解就是切點(diǎn)表達(dá)式,匹配對應(yīng)的連接點(diǎn),通知,指在切面的某個特定的連接點(diǎn)上執(zhí)行的動作。,織入,將作用在的過程。因?yàn)樵创a都是英文寫的。 之前《零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)》詳細(xì)講了Spring容器的初始化和加載的原理,后面《你真的完全了解Java動態(tài)代理嗎?看這篇就夠了》介紹了下...
摘要:本文首發(fā)于作者最近在學(xué),研究了下和代理模式,寫點(diǎn)心得和大家分享下。所以下面來重點(diǎn)分析下代理模式。這里代理模式分為靜態(tài)代理和動態(tài)代理兩種,我們分別來看下。代理模式,代理,意味著有一方代替另一方完成一件事。 本文首發(fā)于 https://jaychen.cc作者 jaychen 最近在學(xué) Spring,研究了下 AOP 和代理模式,寫點(diǎn)心得和大家分享下。 AOP 先說下AOP,AOP 全稱 ...
摘要:與靜態(tài)代理對比,動態(tài)代理是在動態(tài)生成代理類,由代理類完成對具體方法的封裝,實(shí)現(xiàn)的功能。本文將分析中兩種動態(tài)代理的實(shí)現(xiàn)方式,和,比較它們的異同。那如何動態(tài)編譯呢你可以使用,這是一個封裝了的庫,幫助你方便地實(shí)現(xiàn)動態(tài)編譯源代碼。 發(fā)現(xiàn)Java面試很喜歡問Spring AOP怎么實(shí)現(xiàn)的之類的問題,所以寫一篇文章來整理一下。關(guān)于AOP和代理模式的概念這里并不做贅述,而是直奔主題,即AOP的實(shí)現(xiàn)方...
摘要:又是什么其實(shí)就是一種實(shí)現(xiàn)動態(tài)代理的技術(shù),利用了開源包,先將代理對象類的文件加載進(jìn)來,之后通過修改其字節(jié)碼并且生成子類。 在實(shí)際研發(fā)中,Spring是我們經(jīng)常會使用的框架,畢竟它們太火了,也因此Spring相關(guān)的知識點(diǎn)也是面試必問點(diǎn),今天我們就大話Aop。特地在周末推文,因?yàn)樵撈恼麻喿x起來還是比較輕松詼諧的,當(dāng)然了,更主要的是周末的我也在充電學(xué)習(xí),希望有追求的朋友們也盡量不要放過周末時...
摘要:之后通過類的靜態(tài)方法取得一個代理類實(shí)例再次鄙視自己。值得一提,動態(tài)代理把也代理了??偨Y(jié)動態(tài)代理優(yōu)點(diǎn)相比靜態(tài)代理,不用每代理一個類就得寫一個新的代理類。缺點(diǎn)只能代理實(shí)現(xiàn)了接口的類,因?yàn)槭菃卫^承,代理類已經(jīng)是類的子類了。 動態(tài)代理 這里暫時只做JDK動態(tài)代理分析。動態(tài)代理應(yīng)用廣泛,例如AOP。 showImg(https://segmentfault.com/img/bVUmAr?w=21...
閱讀 1129·2021-11-23 10:05
閱讀 1809·2021-11-12 10:36
閱讀 1868·2019-08-30 15:56
閱讀 1699·2019-08-29 12:32
閱讀 3057·2019-08-28 18:04
閱讀 3444·2019-08-26 12:17
閱讀 2516·2019-08-26 11:35
閱讀 1259·2019-08-23 15:11