淺談Java代理一:JDK動態代理-Proxy.newProxyInstance
- java.lang.reflect.Proxy:該類用於動態生成代理類,只需傳入目標接口、目標接口的類加載器以及InvocationHandler便可為目標接口生成代理類及代理對象。
// 方法 1: 該方法用於獲取指定代理對象所關聯的InvocationHandler static InvocationHandler getInvocationHandler(Object proxy) // 方法 2:該方法用於獲取關聯於指定類裝載器和一組接口的動態代理類的類對象 static Class getProxyClass(ClassLoader loader, Class[] interfaces) // 方法 3:該方法用於判斷指定類是否是一個動態代理類 static boolean isProxyClass(Class cl) // 方法 4:該方法用於為指定類裝載器、一組接口及調用處理器生成動態代理類實例 static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
JDK中的動態代理是通過反射類Proxy以及InvocationHandler回調接口實現的;但是,JDK中所要進行動態代理的類必須要實現一個接口,也就是說只能對該類所實現接口中定義的方法進行代理,這在實際編程中具有一定的局限性,而且使用反射的效率也並不是很高。
要生成某一個對象的代理對象,這個代理對象通常也要編寫一個類來生成,所以首先要編寫用於生成代理對象的類。在java中如何用程序去生成一個對象的代理對象呢,java在JDK1.5之后提供了一個"java.lang.reflect.Proxy"類,通過"Proxy"類提供的一個newProxyInstance方法用來創建一個對象的代理對象,如下所示:
示例業務邏輯:
1-娛樂明星都會唱歌、演習(interface Star)
2-有一個明星叫胡歌(class HuGe implements Star)
3-他有兩個助理(分別對應兩個代理類)(class HuGeProxy1、class HuGeProxy2)
4-如果要找胡歌唱歌、演戲,需要先找兩個助理中的一個,然后助理去找胡歌唱歌、演戲(class ProxyTest)
package com.huishe.testOfSpring.proxy; //定義一個明細接口 public interface Star { void sing(String song);//唱歌 String act(String teleplay);//表演 }
package com.huishe.testOfSpring.proxy; //創建胡歌類-實現明細接口 public class HuGe implements Star{ public void sing(String song) { System.out.println("胡歌演唱: " + song); } public String act(String teleplay) { System.out.println("胡歌決定出演電視劇: " + teleplay); return "胡歌答應出演電視劇: " + teleplay; } }
package com.huishe.testOfSpring.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; //胡歌代理類1 //使用一個匿名內部類來實現該接口實現InvocationHandler接口,實現invoke方法 public class HuGeProxy1 { private Star hg = new HuGe();//實例化一個對象 public Star getProcxy(){ //使用Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)返回某個對象的代理對象 /** * ClassLoader loader:Java類加載器; 可以通過這個類型的加載器,在程序運行時,將生成的代理類加載到JVM即Java虛擬機中,以便運行時需要! * Class<?>[] interfaces:被代理類的所有接口信息; 便於生成的代理類可以具有代理類接口中的所有方法 * InvocationHandler h:調用處理器; 調用實現了InvocationHandler 類的一個回調方法 * */ return (Star)Proxy.newProxyInstance( getClass().getClassLoader(), hg.getClass().getInterfaces(), new InvocationHandler() { /** * InvocationHandler接口只定義了一個invoke方法,因此對於這樣的接口,我們不用單獨去定義一個類來實現該接口, * 而是直接使用一個匿名內部類來實現該接口,new InvocationHandler() {}就是針對InvocationHandler接口的匿名實現類 */ /** * 在invoke方法編碼指定返回的代理對象干的工作 * proxy : 把代理對象自己傳遞進來 * method:把代理對象當前調用的方法傳遞進來 * args:把方法參數傳遞進來 * * 當調用代理對象的star.sing("逍遙嘆");或者 star.act("琅琊榜")方法時, * 實際上執行的都是invoke方法里面的代碼, * 因此我們可以在invoke方法中使用method.getName()就可以知道當前調用的是代理對象的哪個方法 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("sing")){ System.out.println("我是胡歌代理1,找胡歌唱歌找我"); return method.invoke(hg, args); } if(method.getName().equals("act")){ System.out.println("我是胡歌代理1,找胡歌演電視劇找我"); return method.invoke(hg, args); } return null; } }); } }
package com.huishe.testOfSpring.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; //胡歌代理類2 //實現InvocationHandler接口,實現invoke方法 public class HuGeProxy2 implements InvocationHandler{ private Star hg = new HuGe(); public Star getProcxy(){ return (Star)Proxy.newProxyInstance( getClass().getClassLoader(), hg.getClass().getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("sing")){ System.out.println("我是胡歌代理2,找胡歌唱歌找我"); return method.invoke(hg, args); } if(method.getName().equals("act")){ System.out.println("我是胡歌代理2,找胡歌演電視劇找我"); return method.invoke(hg, args); } return null; } }
package com.huishe.testOfSpring.proxy; import org.junit.Test; public class ProxyTest { //測試代理類1 @Test public void testHuGeProxy1(){ HuGeProxy1 proxy = new HuGeProxy1();//找到胡歌的助理 Star hg = proxy.getProcxy();//助理和胡歌洽談 hg.sing("《逍遙嘆》");//(胡歌答應后)唱歌 String actResult = hg.act("《琅琊榜》");//(胡歌答應后)演習 System.out.println("演出結果:" + actResult); } //測試代理類1 @Test public void testHuGeProxy2(){ HuGeProxy2 proxy = new HuGeProxy2(); Star hg = proxy.getProcxy(); hg.sing("《逍遙嘆》"); String actResult = hg.act("《琅琊榜》"); System.out.println("演出結果:" + actResult); } }
代理1日志輸出:
我是胡歌代理1,找胡歌唱歌找我
胡歌演唱: 《逍遙嘆》
我是胡歌代理1,找胡歌演電視劇找我
胡歌決定出演電視劇: 《琅琊榜》
演出結果:胡歌答應出演電視劇: 《琅琊榜》
代理2日志輸出:
我是胡歌代理2,找胡歌唱歌找我
胡歌演唱: 《逍遙嘆》
我是胡歌代理2,找胡歌演電視劇找我
胡歌決定出演電視劇: 《琅琊榜》
演出結果:胡歌答應出演電視劇: 《琅琊榜》
參考資料:
1-https://www.cnblogs.com/xdp-gacl/p/3971367.html
2-https://blog.csdn.net/justloveyou_/article/details/79407248