Java中的代理的使用
Java中的代理
代理解釋:一種設計模式,代理類與委托類(被代理對象、目標對象)有同樣的接口,代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事后處理消息等。代理類與委托類之間通常會存在關聯關系,一個代理類的對象與一個委托類的對象關聯,代理類的對象本身并不真正實現服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。
通俗解釋:不直接調運目標對象,直接調運代理對象,讓代理對象去調運目標對象。
代理目的:當調運代理對象,然后在代理對象調運目標對象之前或者調運目標對象以后,我們可以干一些事,比如權限控制,日志記錄等。
代理分類:靜態代理,動態代理
舉例分析:
程序說明:有剛學說話的個小朋友想說“我是中國人”,但是他不能完整的說出來,只能說“中國”兩個字,那么我們就使用代理模式,讓他的爸爸媽媽幫他把他想說的話補充完整。他的爸爸幫他說:“我是”,他的媽媽幫他說:“人”。然后即使小朋友只說了“中國”,但是我們聽見的就是“我是中國人”。
這其中小朋友就是被代理對象、目標對象,他的爸爸媽媽就是代理對象。
首先建立一個Person接口:
public interface Person { public void say(); }
建立一個接口的實現類:
public class PersonImpl implements Person { @Override public void say() { System.out.print("中國"); } }
再建立一個沒有實現接口的類:
public class PersonNoImpl { public void say() { System.out.print("中華人民共和國"); } }
1.靜態代理:在程序運行前,代理類的.class文件就已經存在了。
public class PersonProxy implements Person { private Person person;// 被代理對象public PersonProxy(Person p) { this.person = p; }
@Override public void say() { System.out.print("我是-"); person.say();// 在目標方法前后分別添加操作 System.out.println("-人"); }
}</pre>
這種方式的最大缺點就是每次我們都需要建立不同的代理對象,靈活性和可復用性都很差,所以我們需要使用到動態代理技術。
2.動態代理:在程序運行時,運用反射機制動態創建而成。
有兩種實現方式:JDK代理,CGLIB代理
方式1:使用jdk提供的代理對象(代理對象需要實現InvocationHandler接口,目標對象必須實現其他接口)
public class PersonProxyJdk implements InvocationHandler { private Object targetObj;/ @param obj 被代理對象 @return 代理對象的實例 */ public Object createProxyInstance(Object obj) { this.targetObj = obj; / 參數說明 參數一:ClassLoader,定義代理類的類加載器 參數二:Class<?>,代理類要實現的接口列表 * 參數三:InvocationHandler,指派方法調用的調用處理程序 (指定調運哪個類的invoke方法) **/ return Proxy.newProxyInstance(obj.getClass().getClassLoader(), this.targetObj.getClass().getInterfaces(), this); }
/ 當代理對象的方法被調運時,就會執行回調函數invoke方法,讓這個回調函數再去執行目標代碼的指定方法, 并且會將代理對象接收到的參數傳遞給目標代碼。 這是一個回調函數。 注意:method args 都是由調運代理對象后產生的。所以是確定的。 還可以在這個回調函數中做一些手腳,比如限制調運或者其他。 / @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.print("我是"); result = method.invoke(targetObj, args); System.out.println("人"); return result; }
}</pre>
注意:目標對象必須實現接口。因為需要得到接口的所有方法,然后進行調運接口的所有方法。
方式2:使用cglib提供的代理對象(代理對象需要實現MethodInterceptor接口,目標對象不需要實現接口)
//導包:cglib-nodep-2.2.3.jar public class PersonProxyCglib implements MethodInterceptor { private Object targetObj;/* 生成的代理對象其實就是目標對象的子類 @param obj 被代理對象 @return 代理對象的實例 / public Object createProxyInstance(Object obj) { this.targetObj = obj; Enhancer enhancer = new Enhancer();// 用于生成代理對象 enhancer.setSuperclass(this.targetObj.getClass());// 設置代理對象的父類 enhancer.setCallback(this);// 設置代理對象的回調函數就是本身 return enhancer.create();// 生成代理對象 }
/ 當代理對象的方法被調運時,就會執行改代理對象的回調函數,也就是intercept方法 這個回調函數接收代理對象傳遞來的參數 */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { Object result = null; System.out.print("我是#"); result = proxy.invokeSuper(obj, args); System.out.print("#人"); return result; }
}</pre>
注意:目標對象不須實現接口。因為生成的代理對象時目標對象的子類。
3.測試上面的代理
public class Main { public static void main(String[] args) {Person zhangsan = new PersonImpl();// 被代理的對象
zhangsan.say();// 如果讓代理目標直接執行目標方法,就不能在目標方法前后操作 System.out.println();
PersonProxy proxy = new PersonProxy(zhangsan);// 將被代理的對象傳遞一個代理類 proxy.say();// 讓代理類去執行目標方法,這個時候代理類就在目標方法執行前后亂搞了
PersonProxyJdk proxyJdk = new PersonProxyJdk();// 創建一個代理對象 Person zhangsanJdk = (Person) proxyJdk.createProxyInstance(zhangsan);// 將被代理對象傳遞給代理對象,并且返回被代理接口 zhangsanJdk.say();// 調運被代理對象的接口,就能動態的去執行代理對象想要執行的操作
PersonProxyCglib proxyCglib = new PersonProxyCglib();// 創建Cglib代理對象 Person zhansanCglib = (Person) proxyCglib.createProxyInstance(zhangsan);// 將被代理對象傳遞給代理對象,并且返回被代理接口 zhansanCglib.say();// 調運被代理對象的接口,就能動態的去執行代理對象想要執行的操作 // 使用Cglib的時候,目標對象可以不實現任何接口,但是使用JDK代理的時候就不可以 PersonNoImpl lisi = new PersonNoImpl(); PersonNoImpl lisiCglib = (PersonNoImpl) proxyCglib.createProxyInstance(lisi); lisiCglib.say();
} } 輸出: 中國 我是-中國-人 我是中國人 我是#中國#人 我是#中華人民共和國#人</pre>來自:http://dyygusi.iteye.com/blog/1994843