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