摘要:代理模式代理模式通俗一點的解釋就是在操作一個對象和對象中的方法時,不是直接操作這個對象,還是通過一個代理對象來操作這個實際的目標對象。
代理模式:
代理模式通俗一點的解釋就是在操作一個對象和對象中的方法時,不是直接操作這個對象,還是通過一個代理對象來操作這個實際的目標對象。應(yīng)用場景一般是需要在執(zhí)行某個已經(jīng)寫好的方法前后再添加一段邏輯,比如執(zhí)行方法前打印日志,或者在執(zhí)行方法之前和之后打時間戳來計算方法的執(zhí)行時間,諸如此類的。當然這些操作可以在寫方法的時候就去寫好,但是這樣的話效率非常低,重復代碼復制了一遍又一遍,如果要統(tǒng)一改點什么數(shù)量多起來的話基本上是個不可能完成的任務(wù),而代理模式就是專門解決這種問題的。
靜態(tài)代理:靜態(tài)代理其實代理類Proxy中定義了一個方法,這個方法來調(diào)用被代理類Target中的方法,這樣我們就可以在執(zhí)行這個方法的前后增加邏輯了,代理類和被代理類是組合關(guān)系。這里實現(xiàn)一個接口是為了有更好的擴展性,代理類Proxy中聲明接受這個接口類型,那么被代理類只要實現(xiàn)了這個接口就可以使用代理類Proxy進行代理操作了,這里是JAVA的多態(tài)特性。
被代理的目標的實現(xiàn)接口
public interface TargetImpl { void doSomething(); }
被代理的目標類
public class Target implements TargetImpl { public void doSomething(){ System.out.println("target do something!!!"); } }
代理類
public class Proxy implements TargetImpl { private TargetImpl baseObject; public Proxy(TargetImpl baseObject) { this.baseObject = baseObject; } public void doSomething(){ System.out.println("before method"); baseObject.doSomething(); System.out.println("after method"); } }
測試類:
public class TestMain { public static void main(String[] args){ staticProxy(); } public static void staticProxy(){ Target target = new Target(); Proxy proxy = new Proxy(target); proxy.doSomething(); } }動態(tài)代理:
上面靜態(tài)代理類已經(jīng)幫我們解決了很多冗余代碼,但是存在的問題還是很多,比如一個代理類只能對一種類型目標類有效,換一種類型要新增一個代理類,而且如果有很多地方使用目標類就得在每個地方調(diào)用代理類,很麻煩,而動態(tài)代理則可以解決這種問題。
代理類,需要實現(xiàn)InvocationHandler接口,這個接口是JAVA自帶的,實現(xiàn)invoke()方法,被代理的目標類會在invoke()方法中被調(diào)用,只需要在這個方法中添加邏輯即可。而proxy()方法則是調(diào)用了Proxy.newProxyInstance()方法,這個是JAVA原生類Proxy中的方法,接收目標類的類型參數(shù)和目標類的對象參數(shù)。
// 這個接口是JDK自帶的,所有的代理類都要實現(xiàn)這個接口 // 這樣才能調(diào)用Proxy.newProxyInstance()這個生成代理類的靜態(tài)方法 public class MyProxy implements InvocationHandler { private Object proxy; public MyProxy(Object proxy) { this.proxy = proxy; } // 代理類實現(xiàn)接口中的一個方法,接收參數(shù)分別是被代理的類,要執(zhí)行的方法,執(zhí)行方法的參數(shù),返回則是執(zhí)行方法返回的參數(shù) // 代理對象的所有方法調(diào)用都會轉(zhuǎn)到這個方法中 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before invoke"); Object rTarget = method.invoke(this.proxy, args); System.out.println("after invoke"); return rTarget; } // JDK自帶的生成代理類的靜態(tài)方法,第一個參數(shù)是類加載器 第二個參數(shù)是被代理類的接口 第三個參數(shù)是被代理的對象 // 這個方法內(nèi)部的大致原理就是動態(tài)的加載這個類,然后放到內(nèi)存中,所以不是編譯時期生成的,是運行的時候生成的 public static Object proxy(Class interfaceClazz, Object proxy) { return Proxy.newProxyInstance(interfaceClazz.getClassLoader(), new Class[]{interfaceClazz}, new MyProxy(proxy)); } }
被代理的目標類的實現(xiàn)接口
public interface TargetImpl { void doSomething1(); void doSomething2(); String doSomething3(); }
被代理的目標類
public class Target implements TargetImpl { private String text; public Target(String text) { this.text = text; } public void doSomething1(){ System.out.println("doSomething1-" + text); } public void doSomething2(){ System.out.println("doSomething2-" + text); } public String doSomething3(){ System.out.println("doSomething3-" + text); String result = "doSomething3-" + text; return result; } }
測試類,調(diào)用proxy()方法,把目標類實現(xiàn)接口的字節(jié)碼和目標類的對象傳入,獲得返回的一個代理類對象,然后就可以調(diào)用對應(yīng)的方法,這個時候會發(fā)現(xiàn)方法執(zhí)行前會執(zhí)行前面在invoke()方法中添加的邏輯。
public class TestMain { public static void main(String[] args){ jdkProxy(); } public static void jdkProxy(){ TargetImpl target = (TargetImpl) MyProxy.proxy(TargetImpl.class, new Target("target")); target.doSomething1(); target.doSomething2(); System.out.println(target.doSomething3()); } }
大致原理
動態(tài)代理之所以叫動態(tài)代理就是因為代理類不是在編譯時生成的,而是代碼運行后動態(tài)生成的。
在調(diào)用了Proxy.newProxyInstance()方法之后,因為把目標類實現(xiàn)接口的字節(jié)碼和目標類的對象出入進行了,所以這個方法的源碼做的大致操作就是根據(jù)這個字節(jié)碼和對象來獲取目標類中的方法等各種信息然后動態(tài)的生成一個代理類,而代理類中所有的方法調(diào)用又會中轉(zhuǎn)到invoke()方法中,invoke()方法又再去調(diào)用目標類中的方法,所以只需要在invoke()方法中添加需要添加的邏輯即可。
注意,如果使用JAVA自帶的動態(tài)代理,目標類是一定要實現(xiàn)一個接口才可以的。
cglib代理:cglib是一個開源的庫,可以在運行時動態(tài)的修改和生成字節(jié)碼,原理其實和JAVA原生的動態(tài)代理差不多,但是不同的地方是它是基于被代理的目標類生成一個子類,然后在在子類中重載父類的方法,所以它可以代理沒有接口實現(xiàn)的目標類,這點是和JAVA原生的動態(tài)代理最大的不同之處。
引入maven
cglib cglib 3.2.12
代理類,實現(xiàn)MethodInterceptor接口,在intercept方法中添加額外的邏輯并調(diào)用methodProxy.invokeSuper()方法來執(zhí)行目標類中的方法獲得代理類的對象。
public class Proxy implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before invoke"); Object object = methodProxy.invokeSuper(o,objects); System.out.println("after invoke"); return object; } }
被代理的目標類的實現(xiàn)接口
public interface TargetImpl { void doSomething1(); void doSomething2(); String doSomething3(); }
被代理的目標類
public class Target implements TargetImpl { private String text; public Target(String text) { this.text = text; } public void doSomething1(){ System.out.println("doSomething1-" + text); } public void doSomething2(){ System.out.println("doSomething2-" + text); } public String doSomething3(){ System.out.println("doSomething3-" + text); String result = "doSomething3-" + text; return result; }
測試類
public class TestMain { public static void main(String[] args){ jdkProxy(); } public static void jdkProxy(){ TargetImpl target = (TargetImpl) MyProxy.proxy(TargetImpl.class, new Target("target")); target.doSomething1(); target.doSomething2(); System.out.println(target.doSomething3()); } }
大致原理
可以看到除了代理類和JAVA原生的動態(tài)代理略有不同其他的地方基本是相同的,也是在運行時動態(tài)的生成代理類。
注意,因為前面說了cglib生成的代理類其實是目標類的一個子類,所以被final聲明的類是沒辦法使用cglib的,會拋出java.lang.IllegalArgumentException: Cannot subclass final class cglib.HelloConcret異常,而被final聲明的方法也是沒辦法被重載的,所以會被忽略。
總結(jié):可以看到在這三種代理方式中都有使用到JAVA中多態(tài)的特性。
靜態(tài)代理就是單純簡單的使用了多態(tài)和組合的特性。
JAVA動態(tài)代理和cglib則是再這個基礎(chǔ)上使用了動態(tài)編譯的方式使得擴展性更強,只不過兩者的動態(tài)生成的方式不同,所以注意事項也有所不同。
擴展:spring中就大量使用了兩種動態(tài)代理,AOP切面就是使用了動態(tài)代理,之所以叫切面就是比如說原來A方法、B方法是依次調(diào)用,而現(xiàn)在配置Spring AOP就可以動態(tài)的在A、B方法的前后添加邏輯,這樣就可以在本來依次調(diào)用的A、B方法之間插入新的邏輯,所以叫面向切面編程。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/75194.html
摘要:這種語法,在中被稱為動態(tài)代理。在動態(tài)代理機制中,這個角色只能是接口。動態(tài)代理就是實現(xiàn)的技術(shù)之一。 所謂動態(tài)代理,指的是語言提供的一種語法,能夠?qū)ο笾胁煌椒ǖ恼{(diào)用重定向到一個統(tǒng)一的處理函數(shù)中來。python重寫__getattr__函數(shù)能夠做到這一點,就連世界上最好的語言也提供稱為魔術(shù)方法的__call。這種語法除了能更好的實現(xiàn)動態(tài)代理外,還是RPC框架實現(xiàn)原理的一部分。 動態(tài)代理...
摘要:中的詳解必修個多線程問題總結(jié)個多線程問題總結(jié)有哪些源代碼看了后讓你收獲很多,代碼思維和能力有較大的提升有哪些源代碼看了后讓你收獲很多,代碼思維和能力有較大的提升開源的運行原理從虛擬機工作流程看運行原理。 自己實現(xiàn)集合框架 (三): 單鏈表的實現(xiàn) 自己實現(xiàn)集合框架 (三): 單鏈表的實現(xiàn) 基于 POI 封裝 ExcelUtil 精簡的 Excel 導入導出 由于 poi 本身只是針對于 ...
摘要:我們今天也來做一個萬能遙控器設(shè)計模式適配器模式將一個類的接口轉(zhuǎn)換成客戶希望的另外一個接口。今天要介紹的仍然是創(chuàng)建型設(shè)計模式的一種建造者模式。設(shè)計模式的理論知識固然重要,但 計算機程序的思維邏輯 (54) - 剖析 Collections - 設(shè)計模式 上節(jié)我們提到,類 Collections 中大概有兩類功能,第一類是對容器接口對象進行操作,第二類是返回一個容器接口對象,上節(jié)我們介紹了...
摘要:我們今天也來做一個萬能遙控器設(shè)計模式適配器模式將一個類的接口轉(zhuǎn)換成客戶希望的另外一個接口。今天要介紹的仍然是創(chuàng)建型設(shè)計模式的一種建造者模式。設(shè)計模式的理論知識固然重要,但 計算機程序的思維邏輯 (54) - 剖析 Collections - 設(shè)計模式 上節(jié)我們提到,類 Collections 中大概有兩類功能,第一類是對容器接口對象進行操作,第二類是返回一個容器接口對象,上節(jié)我們介紹了...
閱讀 2665·2021-11-11 16:54
閱讀 3657·2021-08-16 10:46
閱讀 3441·2019-08-30 14:18
閱讀 3034·2019-08-30 14:01
閱讀 2723·2019-08-29 14:15
閱讀 2007·2019-08-29 11:31
閱讀 3083·2019-08-29 11:05
閱讀 2583·2019-08-26 11:54