在Mybatis源碼解析,一步一步從淺入深(五):mapper節點的解析文章的最后部分,我們提到了動態代理的概念,下面我們就簡單了解一下動態代理。
一,概念
代理設計模式的目的就是在不直接操作對象的前提下對對象進行訪問,實現這個目的得方法就是為目標對象創建一個代理(Proxy),通過代理來訪問目標對象。這個設計模式的優點是什么呢?代碼重用,符合開閉原則。
這樣解釋可能會不太好理解,那么接下來就通俗的來說一下代理:
1,不知道你知不知道VPN(也就是俗稱的科學上網),在黨的領導下,為了營造我國健康的的網絡環境國外的一些網站在我國是無法訪問的。例如:google。但是因為某些行業的特殊性需要訪問這些國內不可訪問的網站,那么怎么辦呢?這個時候就需要代理(正向代理)了,怎么實現呢?例如:香港可以訪問國內不可訪問的網站,而國內卻可以訪問香港的網站。那么就可以這樣做“國內->香港服務器(代理)->國外網站”。這里就用到了代理。
2,更通俗的例子就是,作者小的時候被狗狗追着跑,所以到現在作者看到狗狗,特別是體型龐大的狗狗的時候,就會害怕。假如一個狗狗有一天突然搶了作者的包子,那怎么辦呢?那只能委托狗狗的主人,讓狗狗把包子還給作者(r如果狗狗還沒吃掉)。這里的主人也是扮演了代理的角色。
3,再來一個,作者是王力宏的歌迷。也一直很想點歌給王力宏唱。要怎么辦?直接找他,估計沒時間搭理我,最有希望的就是找他的經紀人,通過經紀人,傳達我想點的歌。這個時候這個經紀人就是代理。
二,動態代理代碼示例
那么接下來就以上面說到第三個例子,用代碼展示一下代理模式。代理模式分為靜態代理和動態代理,本文只說明動態代理。
1,王力宏是一個歌手,我們首先創建一個Singer接口類,並聲明一個點歌和告別的方法
1 package com.zcz.proxyTest.testtwo; 2 3 public interface Singer { 4 /** 5 * 根據歌名點歌 6 * @param songName 7 */ 8 public void orderSong(String songName); 9 /** 10 * 向觀眾告別 11 * @param audienceName 12 */ 13 public void sayGoodBye(String audienceName); 14 }
2,接下來實現王力宏類,並實現接口中的兩個方法。
package com.zcz.proxyTest.testtwo; public class WangLiHong implements Singer { @Override public void orderSong(String songName) { // TODO Auto-generated method stub System.out.println("演唱歌曲:"+songName); } @Override public void sayGoodBye(String audienceName) { // TODO Auto-generated method stub System.out.println("再見:"+audienceName); } }
3,就算我找到了經紀人(代理),那我也不能隨便就叫經紀人通知(調用)王力宏(目標對象)唱歌(的方法),我還要准備一個感人肺腑的故事,但是我又不會講故事,這個時候,經紀人就提供給了我一個模板(調用處理類,一個接口),讓我按照模板編寫故事(實現invoke方法)
package com.zcz.proxyTest.testtwo; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyStoryInvocationHandler implements InvocationHandler { private Object object; public MyStoryInvocationHandler(Object o) { // TODO Auto-generated constructor stub this.object = o; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub System.out.println("力宏,我是你代理經紀人:"+proxy.getClass().getName()); //處理業務 System.out.println("巴拉巴拉......(講了一個故事)"); for(Object arg:args) { System.out.println("傳入的參數:"+arg); } //通過反射機制,通知力宏做事情 method.invoke(object, args); //處理業務 System.out.println("巴拉巴拉,感謝........"); return null; } }
代碼中的method.invoke方法使用到了反射機制,想要詳細了解的可以查閱:Java程序語言的后門-反射機制
4,OK,到這里所有的准備工作就完成了,接下來就開始吧。
package com.zcz.proxyTest.testtwo; import java.lang.reflect.Proxy; public class MyPorxy { public static void main(String[] args) { //實例化目標對象(創造一個力宏) WangLiHong liHong = new WangLiHong(); //實例化調用處理類(編好的故事) MyStoryInvocationHandler handler = new MyStoryInvocationHandler(liHong); //接下來創建代理(經紀人) //准備一個類加載器 ClassLoader loader = MyPorxy.class.getClassLoader(); //獲取目標對象的接口類對象數組 Class<?>[] interfaces = liHong.getClass().getInterfaces(); //創建代理 Singer proxy = (Singer) Proxy.newProxyInstance(loader, interfaces, handler); //開始點歌 proxy.orderSong("就是現在"); System.out.println("****** 歌唱中......********"); //歌唱完了,say goodBye吧 proxy.sayGoodBye("zhangchengzi"); } }
5,好,現在右鍵-> Run As -> Java Application。看打印結果:
力宏,我是你代理經紀人:com.sun.proxy.$Proxy0 巴拉巴拉......(講了一個故事) 傳入的參數:就是現在 演唱歌曲:就是現在 巴拉巴拉,感謝........ ****** 歌唱中......******** 力宏,我是你代理經紀人:com.sun.proxy.$Proxy0 巴拉巴拉......(講了一個故事) 傳入的參數:zhangchengzi 再見:zhangchengzi 巴拉巴拉,感謝........
6,看到了嗎?我們不沒有直接使用目標對象的方法,而是構造出一個代理對象,通過代理來訪問了目標對象的方法。總結一下,想要實現動態代理需要四個關鍵元素
1,一個接口A
2,一個繼承接口A,並實現A中抽象方法的實現類
3,一個實現了InvocationHandler的調用處理類
4,調用Proxy.newProxyInstance方法,獲取代理類
7,OK,繼續Mybatis源碼解析吧:Mybatis源碼解析,一步一步從淺入深(五):mapper節點的解析
8,雖然我們實現了一個動態代理的實例,但是你可能還更想清楚的了解動態代理的實現機制,不要着急,正好最近在找工作,有時間我們通過源代碼繼續深入的了解一下動態代理的實現機制。
相關java設計模式的文章:
JAVA設計模式-單例模式(Singleton)線程安全與效率
原創不易,轉載請聲明出處:https://www.cnblogs.com/zhangchengzi/p/9700248.html