動態代理技術是整個java技術中最重要的一個技術,它是學習java框架的基礎,不會動態代理技術,那麼在學習Spring這些框架時是學不明白的。java
動態代理技術就是用來產生一個對象的代理對象的。在開發中爲何須要爲一個對象產生代理對象呢?
舉一個現實生活中的例子:歌星或者明星都有一個本身的經紀人,這個經紀人就是他們的代理人,當咱們須要找明星表演時,不能直接找到該明星,只能是找明星的代理人。好比劉德華在現實生活中很是有名,會唱歌,會跳舞,會拍戲,劉德華在沒有出名以前,咱們能夠直接找他唱歌,跳舞,拍戲,劉德華出名以後,他乾的第一件事就是找一個經紀人,這個經紀人就是劉德華的代理人(代理),當咱們須要找劉德華表演時,不能直接找到劉德華了(劉德華說,你找我代理人商談具體事宜吧!),只能是找劉德華的代理人,所以劉德華這個代理人存在的價值就是攔截咱們對劉德華的直接訪問!
這個現實中的例子和咱們在開發中是同樣的,咱們在開發中之因此要產生一個對象的代理對象,主要用於攔截對真實業務對象的訪問。那麼代理對象應該具備什麼方法呢?代理對象應該具備和目標對象相同的方法app
因此在這裏明確代理對象的兩個概念:
一、代理對象存在的價值主要用於攔截對真實業務對象的訪問。
二、代理對象應該具備和目標對象(真實業務對象)相同的方法。劉德華(真實業務對象)會唱歌,會跳舞,會拍戲,咱們如今不能直接找他唱歌,跳舞,拍戲了,只能找他的代理人(代理對象)唱歌,跳舞,拍戲,一我的要想成爲劉德華的代理人,那麼他必須具備和劉德華同樣的行爲(會唱歌,會跳舞,會拍戲),劉德華有什麼方法,他(代理人)就要有什麼方法,咱們找劉德華的代理人唱歌,跳舞,拍戲,可是代理人不是真的懂得唱歌,跳舞,拍戲的,真正懂得唱歌,跳舞,拍戲的是劉德華,在現實中的例子就是咱們要找劉德華唱歌,跳舞,拍戲,那麼只能先找他的經紀人,交錢給他的經紀人,而後經紀人再讓劉德華去唱歌,跳舞,拍戲。框架
如今要生成某一個對象的代理對象,這個代理對象一般也要編寫一個類來生成,因此首先要編寫用於生成代理對象的類。在java中如何用程序去生成一個對象的代理對象呢,java在JDK1.5以後提供了一個"java.lang.reflect.Proxy"類,經過"Proxy"類提供的一個newProxyInstance方法用來建立一個對象的代理對象,以下所示:ide
1 static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
newProxyInstance方法用來返回一個代理對象,這個方法總共有3個參數,ClassLoader loader用來指明生成代理對象使用哪一個類裝載器,Class<?>[] interfaces用來指明生成哪一個對象的代理對象,經過接口指定,InvocationHandler h用來指明產生的這個代理對象要作什麼事情。因此咱們只須要調用newProxyInstance方法就能夠獲得某一個對象的代理對象了。學習
在java中規定,要想產生一個對象的代理對象,那麼這個對象必需要有一個接口,因此咱們第一步就是設計這個對象的接口,在接口中定義這個對象所具備的行爲(方法)測試
一、定義對象的行爲接口編碼
1 package cn.gacl.proxy; 2 3 /** 4 * @ClassName: Person 5 * @Description: 定義對象的行爲 6 * @author: 孤傲蒼狼 7 * @date: 2014-9-14 下午9:44:22 8 * 9 */ 10 public interface Person { 11 12 /** 13 * @Method: sing 14 * @Description: 唱歌 15 * @Anthor:孤傲蒼狼 16 * 17 * @param name 18 * @return 19 */ 20 String sing(String name); 21 /** 22 * @Method: sing 23 * @Description: 跳舞 24 * @Anthor:孤傲蒼狼 25 * 26 * @param name 27 * @return 28 */ 29 String dance(String name); 30 }
二、定義目標業務對象類spa
1 package cn.gacl.proxy; 2 3 /** 4 * @ClassName: LiuDeHua 5 * @Description: 劉德華實現Person接口,那麼劉德華會唱歌和跳舞了 6 * @author: 孤傲蒼狼 7 * @date: 2014-9-14 下午9:22:24 8 * 9 */ 10 public class LiuDeHua implements Person { 11 12 public String sing(String name){ 13 System.out.println("劉德華唱"+name+"歌!!"); 14 return "歌唱完了,謝謝你們!"; 15 } 16 17 public String dance(String name){ 18 System.out.println("劉德華跳"+name+"舞!!"); 19 return "舞跳完了,多謝各位觀衆!"; 20 } 21 }
三、建立生成代理對象的代理類設計
1 package cn.gacl.proxy; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 import java.lang.reflect.Proxy; 6 7 /** 8 * @ClassName: LiuDeHuaProxy 9 * @Description: 這個代理類負責生成劉德華的代理人 10 * @author: 孤傲蒼狼 11 * @date: 2014-9-14 下午9:50:02 12 * 13 */ 14 public class LiuDeHuaProxy { 15 16 //設計一個類變量記住代理類要代理的目標對象 17 private Person ldh = new LiuDeHua(); 18 19 /** 20 * 設計一個方法生成代理對象 21 * @Method: getProxy 22 * @Description: 這個方法返回劉德華的代理對象:Person person = LiuDeHuaProxy.getProxy();//獲得一個代理對象 23 * @Anthor:孤傲蒼狼 24 * 25 * @return 某個對象的代理對象 26 */ 27 public Person getProxy() { 28 //使用Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)返回某個對象的代理對象 29 return (Person) Proxy.newProxyInstance(LiuDeHuaProxy.class 30 .getClassLoader(), ldh.getClass().getInterfaces(), 31 new InvocationHandler() { 32 /** 33 * InvocationHandler接口只定義了一個invoke方法,所以對於這樣的接口,咱們不用單獨去定義一個類來實現該接口, 34 * 而是直接使用一個匿名內部類來實現該接口,new InvocationHandler() {}就是針對InvocationHandler接口的匿名實現類 35 */ 36 /** 37 * 在invoke方法編碼指定返回的代理對象乾的工做 38 * proxy : 把代理對象本身傳遞進來 39 * method:把代理對象當前調用的方法傳遞進來 40 * args:把方法參數傳遞進來 41 * 42 * 當調用代理對象的person.sing("冰雨");或者 person.dance("江南style");方法時, 43 * 實際上執行的都是invoke方法裏面的代碼, 44 * 所以咱們能夠在invoke方法中使用method.getName()就能夠知道當前調用的是代理對象的哪一個方法 45 */ 46 @Override 47 public Object invoke(Object proxy, Method method, 48 Object[] args) throws Throwable { 49 //若是調用的是代理對象的sing方法 50 if (method.getName().equals("sing")) { 51 System.out.println("我是他的經紀人,要找他唱歌得先給十萬塊錢!!"); 52 //已經給錢了,經紀人本身不會唱歌,就只能找劉德華去唱歌! 53 return method.invoke(ldh, args); //代理對象調用真實目標對象的sing方法去處理用戶請求 54 } 55 //若是調用的是代理對象的dance方法 56 if (method.getName().equals("dance")) { 57 System.out.println("我是他的經紀人,要找他跳舞得先給二十萬塊錢!!"); 58 //已經給錢了,經紀人本身不會唱歌,就只能找劉德華去跳舞! 59 return method.invoke(ldh, args);//代理對象調用真實目標對象的dance方法去處理用戶請求 60 } 61 62 return null; 63 } 64 }); 65 } 66 }
測試代碼:代理
1 package cn.gacl.proxy; 2 3 public class ProxyTest { 4 5 public static void main(String[] args) { 6 7 LiuDeHuaProxy proxy = new LiuDeHuaProxy(); 8 //得到代理對象 9 Person p = proxy.getProxy(); 10 //調用代理對象的sing方法 11 String retValue = p.sing("冰雨"); 12 System.out.println(retValue); 13 //調用代理對象的dance方法 14 String value = p.dance("江南style"); 15 System.out.println(value); 16 } 17 }
運行結果以下:
Proxy類負責建立代理對象時,若是指定了handler(處理器),那麼無論用戶調用代理對象的什麼方法,該方法都是調用處理器的invoke方法。
因爲invoke方法被調用須要三個參數:代理對象、方法、方法的參數,所以無論代理對象哪一個方法調用處理器的invoke方法,都必須把本身所在的對象、本身(調用invoke方法的方法)、方法的參數傳遞進來。
在動態代理技術裏,因爲無論用戶調用代理對象的什麼方法,都是調用開發人員編寫的處理器的invoke方法(這至關於invoke方法攔截到了代理對象的方法調用)。而且,開發人員經過invoke方法的參數,還能夠在攔截的同時,知道用戶調用的是什麼方法,所以利用這兩個特性,就能夠實現一些特殊需求,例如:攔截用戶的訪問請求,以檢查用戶是否有訪問權限、動態爲某個對象添加額外的功能。