java代理模式

 

代理模式:給某一對象提供代理對象,並由代理對象控制具體對象的引用.

代理,指的就是一個角色代表另一個角色採取行動,就象生活中,一個紅酒廠商,是不會直接把紅酒零售客戶的,都是通過代理來完成他的銷售業務的.而客戶,也不用爲了喝紅酒而到處找工廠,他只要找到廠商在當地的代理就行了,具體紅酒工廠在那裏,客戶不用關心,代理會幫他處理.

代理模式涉及的角色:
1:抽象主題角色.聲明瞭代理主題和真實主題的公共接口,使任何需要真實主題的地方都能用代理主題代替.

2:代理主題角色.含有真實主題的引用,從而可以在任何時候操作真實主題,代理主題功過提供和真實主題相同的接口,使它可以隨時代替真實主題.代理主題通過持有真實主題的引用,不但可以控制真實主題的創建或刪除,可以在真實主題被調用前進行攔截,或在調用後進行某些操作.

3:真實代理對象.定義了代理角色所代表的具體對象.

下面是代理模式的實現類圖: 
 
 


根據上圖的關係,我們可以用客戶買紅酒來模擬代理模式的實現,

紅酒代理商和紅酒廠商都有銷售紅酒的只能,我們可以爲他們定義一個共同的抽象主題角色,
 

Java代碼 
  1. /**  
  2. *抽象主題角色,定義了真實角色和代理角色的公共接口  
  3. */  
  4. public interface SellInterface{   
  5.      public Object sell();   
  6. }  


接着,我們定義真實主題角色(這裏就是紅酒工廠),它必須實現了SellInterface接口的.
 
Java代碼  
  1. /**  
  2. *真實主題角色,這裏指紅酒工廠角色,它實現了SellInterface接口  
  3. */  
  4. public class RedWineFactory implements SellInterface{   
  5.      public Object sell(){   
  6.          System.out.println("真實主題角色RedWineFactory 被調用了");   
  7.          return new Object();   
  8.      }   
  9. }  



下面是代理主題角色(這裏指紅酒代理商),同樣,代理主題也必須實現SellInterface接口.
 

Java代碼  
  1. /**  
  2. *代理主題角色,這裏指紅酒代理商.它除了也要實現了sellInterface接口外,還持有紅酒  
  3. *廠商RedWineFactory 對象的引用,從而使它能在調用真實主題前後做一些必要處理.  
  4. */  
  5. public class RedWineProxy implements SellInterface{   
  6.      //持有一個RedWineFactory對象的引用   
  7.       private RedWineFactory redWineFactory;   
  8.   
  9.      //銷售總量   
  10.       private static int sell_count = 0;   
  11.   
  12.      public Object sell(){   
  13.          if(checkUser()){//在通過代理主題角色,我們可以在真實主題角色被調用前做一些諸如權限判斷的事情   
  14.              Object obj = redWineFactory.sell();   
  15.              count ++;//同樣,在調用後我們也可以執行一些額外的動作.   
  16.              return obj ;   
  17.          }else{   
  18.              throw new RuntimeException();   
  19.          }   
  20.      }   
  21.   
  22.      protected boolean checkUser(){   
  23.             //do something   
  24.             return true;   
  25.      }   
  26. }  



接下來看看調用代理對象的代碼
 

Java代碼  
  1. public static void main(String agr[])   
  2. {   
  3.      SellInterface sell = new RedWineProxy();   
  4.      sell.sell();   
  5. }  



從上面的例子可以看出代理模式的工作方式,首先,因爲代理主題和真實主題都實現了共同的接口,這使我們可以在不改變原來接口的情況下,只要用真實主題對象的地方,都可以用代理主題來代替.其次,代理主題在客戶和真實主題之間起了一箇中介作用,利用這個中介平臺,我們可以在把客戶請求傳遞給真實主題之前,做一些必要的預處理.

java對代理模式的支持 ---動態代理
上面的代理,我們強迫代理類RedWineProxy實現了抽象接口SellInterface.這導致我們的代理類無法通用於其他接口,所以不得不爲每一個接口實現一個代理類.幸好,java爲代理模式提供了支持.
java主要是通過Proxy類和InvocationHandler接口來給實現對代理模式的支持的.
下面用java的代理機制來實現上面的例子
 

Java代碼  
  1. import java.lang.reflect.InvocationHandler;   
  2. import java.lang.reflect.Method;   
  3. import java.lang.reflect.Proxy;   
  4. /**  
  5. *代理類一定要實現了InvocationHandler接口  
  6. */  
  7. public class ProxyObject implements InvocationHandler{   
  8.     private Object proxy_obj;   
  9.   
  10.     ProxyObject(Object obj){   
  11.         this.proxy_obj = obj;   
  12.     }   
  13.   
  14.     public static Object factory(Object obj){   
  15.         Class cls = obj.getClass();   
  16.         //通過Proxy類的newProxyInstance方法來返回代理對象   
  17.         return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),new ProxyObject(obj));   
  18.     }   
  19.   
  20. /**  
  21. *實現InvocationHandler接口的invoke  
  22. */  
  23.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {   
  24.         System.out.println("函數調用前被攔截了:   " + method);   
  25.         if(args != null){   
  26.             //打印參數列表   
  27.             System.out.println("方法有  " + args.length + "    個參數");   
  28.             for(int i = 0; i < args.length; i ++){   
  29.                 System.out.println(args[i]);   
  30.             }   
  31.         }   
  32.         //利用反射機制動態調用原對象的方法   
  33.          Object mo = method.invoke(proxy_obj, args);   
  34.         System.out.println("函數調用後進行處理 :   " + method);   
  35.         return mo;   
  36.     }   
  37.   
  38.     //測試代碼       
  39.     public static void main(String agr[]){   
  40.         SellInterface si = (SellInterface)factory(new RedWineFactory());   
  41.         si.sell();   
  42.     }   
  43. }  


通過上面的代碼可以看出,代理主題ProxyObject類並沒有實現我們定義的SellInterface藉口,
而是實現了java的InvocationHandler接口,這樣就把代理主題角色和我們的業務代碼分離開來,使代理對象能通用於其他接口.
其實InvocationHandler接口就是一種攔截機制,當系統中有了代理對象以後,對原對象(真實主題)方法的調用,都會轉由InvocationHandler接口來處理,並把方法信息以參數的形式傳遞給invoke方法,這樣,我們就可以在invoke方法中攔截原對象的調用,並通過反射機制來動態調用原對象的方法.這好象也是spring aop編程的基礎吧

接着,用代理模式實現一個超級簡單的aop攔截機制
這個例子可以攔截我們指定的函數,並在攔截前後根據需要進行處理
 

Java代碼  
  1. /**  
  2. *切面接口,通過實現這個接口,我們可以對指定函數在調用前後進行處理  
  3. */  
  4. public interface AopInterface {   
  5.    public void before(Object obj);//調用的處理   
  6.    public void end(Object obj);//調用後的處理   
  7. }  



這個是實現了AopInterface 接口,在這裏我們實現了我們的處理邏輯
 

Java代碼  
  1. public class AopInterfaceImp implements AopInterface{   
  2.   
  3.     public void before(Object obj) {   
  4.         System.out.println("調用前攔截");   
  5.     }   
  6.   
  7.     public void end(Object obj) {   
  8.         System.out.println("調用調用後處理");   
  9.     }   
  10.   
  11. }  



接着是代理類
 

Java代碼  
  1. public class PeoxyObject implements InvocationHandler {   
  2.     private AopInterface aop;//定義了切入時調用的方法   
  3.     private Object proxy_obj;   
  4.     private String methodName;//指定要切入的方法名   
  5.   
  6.     PeoxyObject(){}   
  7.   
  8.     public Object factory(Object obj){   
  9.         proxy_obj = obj;   
  10.         Class cls = obj.getClass();   
  11.         return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),this);   
  12.     }   
  13.   
  14.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {   
  15.         if(this.aop == null)throw new NullPointerException("aop is null");   
  16.         if(method == null)throw new NullPointerException("method is null");   
  17.   
  18.         Object o;   
  19. //如果指定了要攔截方法名,並且調用的方法和指定的方法名相同,則進行攔截處理   
  20. //否則當正常方法處理   
  21.         if(methodName != null && method.toString().indexOf(methodName) != -1){   
  22.             aop.before(proxy_obj);//指定方法調用前的處理   
  23.             o = method.invoke(proxy_obj, args);   
  24.             aop.end(proxy_obj);//指定方法調用後的處理   
  25.         }else{   
  26.             //沒有指定的方法,以正常方法調用   
  27.             o = method.invoke(proxy_obj, args);   
  28.         }   
  29.         return o;   
  30.     }   
  31.   
  32.     public AopInterface getAop() {   
  33.         return aop;   
  34.     }   
  35.   
  36.     public void setAop(AopInterface aop) {   
  37.         this.aop = aop;   
  38.     }   
  39.   
  40.     public String getMethodName() {   
  41.         return methodName;   
  42.     }   
  43.   
  44.     public void setMethodName(String methodName) {   
  45.         this.methodName = methodName;   
  46.     }   
  47. }  



這裏定義一個用來測試用的類
 

Java代碼  
  1. public interface SubInterface {   
  2.     public void add(String value1,String value2);   
  3.     public void acc(String value1);   
  4. }   
  5.   
  6. public class ImpObject implements SubInterface{   
  7.   
  8.     public void add(String value1,String value2) {   
  9.         System.out.println("ImpObject add(String value1,String value2)");   
  10.     }   
  11.   
  12.     public void acc(String value1){   
  13.         System.out.println("ImpObject acc(String value1)");   
  14.     }   
  15.   
  16. }  



這裏是測試代碼
 

Java代碼  
  1. public static void main(String agr[]){   
  2.      PeoxyObject po = new PeoxyObject();   
  3.   
  4.      po.setAop(new AopInterfaceImp());//我們實現的攔截處理對象   
  5.       po.setMethodName("acc");//指定要攔截的函數   
  6.         
  7.       SubInterface si = (SubInterface)po.factory(new ImpObject());   
  8.    //因爲add方法不是我們指定的攔截函數,AopInterfaceImp是不會被執行   
  9.       si.add("tt","dd");      
  10.   
  11.    //acc是我們指定的攔截方法,所以調用acc的前後會先執行AopInterfaceImp   
  12.     //對象的兩個方法   
  13.       si.acc("tt");   
  14.  }  


通過上面可以看出,攔截機制是代理模式的重要使用方式之一,
除了攔截,代理模式還常用於資源加載,當我們要加載的資源很大時,我們可以讓真實主題角色在後臺加載資源,讓代理主題角色負責處理前臺的等待提示信息.
還有就是授權機制,通過代理能攔截真實主題的能力,來控制真實主題的訪問權限.

出自:http://www.iteye.com/topic/517835

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章