不定期整理硬盤內(nèi)源代碼、筆記、總結(jié)等,同時發(fā)上來分享一下。今天再發(fā)一篇關(guān)于Java動態(tài)代理的總結(jié)(貌似ItEye一天最多發(fā)5篇Blog,再多只能放草稿箱了?)
-----------------------------------------------------------
Java動態(tài)代理詳解
說到動態(tài)代理,顧名思義就是動態(tài)的代理(真是廢話)。
關(guān)于代理:想必大家都并不陌生,GOF的23種設(shè)計模式之一(結(jié)構(gòu)型模式)。這里暫不多做介紹,有興趣的可以關(guān)注我關(guān)于設(shè)計模式的文章。
什么是動態(tài)代理:
說起動態(tài),其實不如先說什么是靜態(tài)。所謂靜態(tài)代理,個人理解為自己手寫的代理類,或者用工具生成的代理類,或者別人幫你寫的代理類(沒說一樣...)??傊?,就是程序運行前就已經(jīng)存在的編譯好的代理類。
相反,如果代理類程序運行前并不存在,需要在程序運行時動態(tài)生成(無需手工編寫代理類源碼),那就是今天要說的動態(tài)代理了。
如何生成的:根據(jù)Java的反射機制動態(tài)生成。
不多說了,上程序。
目標接口TargetInterface:
- public interface TargetInterface {
- public int targetMethodA(int number);
- public int targetMethodB(int number);
- }
很簡單,一個普通的接口,里面有若干方法(此處寫2個示范一下)
實現(xiàn)該接口的委托類ConcreteClass:
- public class ConcreteClass implements TargetInterface{
-
- public int targetMethodA(int number) {
- System.out.println("開始調(diào)用目標類的方法targetMethodA...");
- System.out.println("操作-打印數(shù)字:"+number);
- System.out.println("結(jié)束調(diào)用目標類的方法targetMethodA...");
- return number;
- }
-
- public int targetMethodB(int number){
- System.out.println("開始調(diào)用目標類的方法targetMethodB...");
- System.out.println("操作-打印數(shù)字:"+number);
- System.out.println("結(jié)束調(diào)用目標類的方法targetMethodB...");
- return number;
- }
-
- }
很簡單,一個普通的類,實現(xiàn)了目標接口。
代理處理器類ProxyHandler:
- public class ProxyHandler implements InvocationHandler{
- private Object concreteClass;
-
- public ProxyHandler(Object concreteClass){
- this.concreteClass=concreteClass;
- }
-
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("proxy:"+proxy.getClass().getName());
- System.out.println("method:"+method.getName());
- System.out.println("args:"+args[0].getClass().getName());
-
- System.out.println("Before invoke method...");
- Object object=method.invoke(concreteClass, args);//普通的Java反射代碼,通過反射執(zhí)行某個類的某方法
- //System.out.println(((ConcreteClass)concreteClass).targetMethod(10)+(Integer)args[0]);
- System.out.println("After invoke method...");
- return object;
- }
-
- }
該類實現(xiàn)了Java反射包中的InvocationHandler接口。代理實例調(diào)用方法時,將對方法調(diào)用指派到它的代理處理器程序的invoke方法中。invoke方法內(nèi)部實現(xiàn)預(yù)處理,對委托類方法調(diào)用,事后處理等邏輯。
最后是入口程序:
- public class DynamicProxyExample {
- public static void main(String[] args){
- ConcreteClass c=new ConcreteClass();//元對象(被代理對象)
- InvocationHandler ih=new ProxyHandler(c);//代理實例的調(diào)用處理程序。
- //創(chuàng)建一個實現(xiàn)業(yè)務(wù)接口的代理類,用于訪問業(yè)務(wù)類(見代理模式)。
- //返回一個指定接口的代理類實例,該接口可以將方法調(diào)用指派到指定的調(diào)用處理程序,如ProxyHandler。
- TargetInterface targetInterface=
- (TargetInterface)Proxy.newProxyInstance(c.getClass().getClassLoader(),c.getClass().getInterfaces(),ih);
- //調(diào)用代理類方法,Java執(zhí)行InvocationHandler接口的方法.
- int i=targetInterface.targetMethodA(5);
- System.out.println(i);
- System.out.println();
- int j=targetInterface.targetMethodB(15);
- System.out.println(j);
- }
- }
首先創(chuàng)建委托類對象,將其以構(gòu)造函數(shù)傳入代理處理器,代理處理器ProxyHandler中會以Java反射方式調(diào)用該委托類對應(yīng)的方法。然后使用Java反射機制中的Proxy.newProxyInstance方式創(chuàng)建一個代理類實例,創(chuàng)建該實例需要指定該實例的類加載器,需要實現(xiàn)的接口(即目標接口),以及處理代理實例接口調(diào)用的處理器。
最后,調(diào)用代理類目標接口方法時,會自動將其轉(zhuǎn)發(fā)到代理處理器中的invoke方法內(nèi),invoke方法內(nèi)部實現(xiàn)預(yù)處理,對委托類方法調(diào)用,事后處理等邏輯。
使用Java動態(tài)代理機制的好處:
1、減少編程的工作量:假如需要實現(xiàn)多種代理處理邏輯,只要寫多個代理處理器就可以了,無需每種方式都寫一個代理類。
2、系統(tǒng)擴展性和維護性增強,程序修改起來也方便多了(一般只要改代理處理器類就行了)。
使用Java動態(tài)代理機制的限制:
目前根據(jù)GOF的代理模式,代理類和委托類需要都實現(xiàn)同一個接口。也就是說只有實現(xiàn)了某個接口的類可以使用Java動態(tài)代理機制。但是,事實上使用中并不是遇到的所有類都會給你實現(xiàn)一個接口。因此,對于沒有實現(xiàn)接口的類,目前無法使用該機制。有人說這不是廢話嗎,本來Proxy模式定義的就是委托類要實現(xiàn)接口的?。〉菦]有實現(xiàn)接口的類,該如何實現(xiàn)動態(tài)代理呢?
當然不是沒有辦法,這也是我后面抽時間要繼續(xù)整理和總結(jié)原先使用過的一件神器,相關(guān)Blog會不定期發(fā)上來。那就是大名鼎鼎的CGLib...
PS:CGLib中的動態(tài)代理已經(jīng)新鮮出爐,歡迎訪問:http://shensy.iteye.com/blog/1873155