隨筆-126  評論-247  文章-5  trackbacks-0

          動態代理可以提供對另一個對象的訪問,同時隱藏實際對象的具體事實,代理對象對客戶隱藏了實際對象。動態代理可以對請求進行其他的一些處理,在不允許直接訪問某些類,

          或需要對訪問做一些特殊處理等,這時候可以考慮使用代理。目前 Java 開發包中提供了對動態代理的支持,但現在只支持對接口的實現。
           
          主要是通過 java.lang.reflect.Proxy 類和 java.lang.reflect.InvocationHandler 接口。 Proxy 類主要用來獲取動態代理對象,InvocationHandler 接口用來約束調用者行為。

          “寫一個 ArrayList 類的代理,其內部實現和 ArrayList 中完全相同的功能,并可以計算每個方法運行的時間。”這是一份考題上的題目,沒有答案,來看下實現:


          package example;

          import java.lang.reflect.InvocationHandler;
          import java.lang.reflect.Method;
          import java.lang.reflect.Proxy;
          import java.util.ArrayList;
          import java.util.List;
          import java.util.concurrent.TimeUnit;
          /**
           * -----------------------------------------
           * @描述  TODO
           * @作者  fancy
           * @郵箱  fancydeepin@yeah.net
           * @日期  2012-8-27 <p>
           * -----------------------------------------
           
          */
          public class ProxyApp {


              
          public static void main(String[] args){
                  
                  
          //ArrayList代理,通過代理計算每個方法調用所需時間
                  List<Integer> arrayListProxy = (List<Integer>)Proxy.newProxyInstance(
                      ArrayList.
          class.getClassLoader(),   /*定義代理類的類加載器,用于創建代理對象,不一定必須是ArrayList,也可以是其他的類加載器*/
                      ArrayList.
          class.getInterfaces(),     /*代理類要實現的接口列表*/
                      
          new InvocationHandler() {            /*指派方法調用的調用處理程序,這里用了匿名內部類*/
                          
                          
          private ArrayList<Integer> target = new ArrayList<Integer>(); //目標對象(真正操作的對象)
                          /**
                           * <B>方法描述:</B>
                           * <p style="margin-left:20px;color:#A52A2A;">
                           * 在代理實例上處理方法調用并返回結果
                           * 
          @param proxy     代理對象(注意不是目標對象)
                           * 
          @param method  被代理的方法
                           * 
          @param args         被代理的方法的參數集
                           * 
          @return <span style="color: #008080;"> 返回方法調用結果 </span>
                           
          */
                          @Override
                          
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                              
                              
          long beginTime = System.currentTimeMillis();  //開始時間
                              TimeUnit.MICROSECONDS.sleep(
          1);
                              Object obj 
          = method.invoke(target, args);          //實際調用的方法,并接受方法的返回值
                              
          long endTime = System.currentTimeMillis();   //結束時間
                              System.out.println(
          "[" + method.getName() + "] spend " + (endTime - beginTime) + " ms");
                              
          return obj;   //返回實際調用的方法的返回值
                              
                          }
                          
                      }
                  );
                  arrayListProxy.add(
          2);
                  arrayListProxy.add(
          4);
                  System.out.println(
          "--------- 迭代 ---------");
                  
          for(int i : arrayListProxy){
                      System.out.print(i 
          + "\t");
                  }
              }
          }

          后臺打印輸出結果:


          [add] spend 
          2 ms
          [add] spend 
          1 ms
          --------- 迭代 ---------
          [iterator] spend 
          1 ms
          2    4    

          從代碼上來看,用到了匿名內部類,這樣一來,InvocationHandler 只能用一次,如果多個地方都需要用到這樣一個相同的 InvocationHandler,可以將其抽象出來成為一個單獨的類:


          package test;

          import java.lang.reflect.InvocationHandler;
          import java.lang.reflect.Method;
          import java.util.concurrent.TimeUnit;

          public class MyInvocationHandler implements InvocationHandler{

              
          private Object target; //目標對象
              
              
          public MyInvocationHandler(Object target){
                  
                  
          this.target = target;
              }
              
              @Override
              
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                  
                  
          long beginTime = System.currentTimeMillis();
                  TimeUnit.MICROSECONDS.sleep(
          1);
                  Object obj 
          = method.invoke(target, args);
                  
          long endTime = System.currentTimeMillis();
                  System.out.println(
          "[" + method.getName() + "] spend " + (endTime - beginTime) + " ms");
                  
          return obj;
                  
              }

          }


          客戶端調用改成:


          package example;

          import java.lang.reflect.Proxy;
          import java.util.ArrayList;
          import java.util.List;
          /**
           * -----------------------------------------
           * @描述  TODO
           * @作者  fancy
           * @郵箱  fancydeepin@yeah.net
           * @日期  2012-8-27 <p>
           * -----------------------------------------
           
          */
          public class ProxyApp {


              
          public static void main(String[] args){
                  
                  
          //ArrayList代理,通過代理計算每個方法調用所需時間
                  List<Integer> arrayListProxy = (List<Integer>)Proxy.newProxyInstance(
                      ArrayList.
          class.getClassLoader(),     /*定義代理類的類加載器,用于創建代理對象,不一定必須是ArrayList,也可以是其他的類加載器*/
                      ArrayList.
          class.getInterfaces(),       /*代理類要實現的接口列表*/
                      
          new MyInvocationHandler(new ArrayList<Integer>())         /*指派方法調用的調用處理程序,這里用了匿名內部類*/
                  );
                  arrayListProxy.add(
          2);
                  arrayListProxy.add(
          4);
                  System.out.println(
          "--------- 迭代 ---------");
                  
          for(int i : arrayListProxy){
                      System.out.print(i 
          + "\t");
                  }
              }
          }

          從上面代碼看來,客戶端知道代理的實際目標對象,還知道怎么樣去創建這樣一個代理對象,如果想把這些信息全部對客戶端隱藏起來,可以將這些代碼挪到一個類中,將它們封裝起來:


          package example;

          import java.lang.reflect.InvocationHandler;
          import java.lang.reflect.Method;
          import java.lang.reflect.Proxy;
          import java.util.ArrayList;
          import java.util.List;
          import java.util.concurrent.TimeUnit;

          /**
           * -----------------------------------------
           * @描述  TODO
           * @作者  fancy
           * @郵箱  fancydeepin@yeah.net
           * @日期  2012-8-27 <p>
           * -----------------------------------------
           
          */
          public class ProxyUtil {

              
          public enum ArrayListProxy {
                  PROXY;
                  
                  
          private Object target;
                  
                  ArrayListProxy(){
                      
          this.target = new ArrayList<Object>();
                  }
                  
                  
          public List getInstance(){
                      
                      
          return (List)Proxy.newProxyInstance(ArrayList.class.getClassLoader(), ArrayList.class.getInterfaces(),
                              
          new InvocationHandler() {
                                  
                                  @Override
                                  
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                      
                                      
          long beginTime = System.currentTimeMillis();
                                      TimeUnit.MICROSECONDS.sleep(
          1);
                                      Object obj 
          = method.invoke(target, args);
                                      
          long endTime = System.currentTimeMillis();
                                      System.out.println(
          "[" + method.getName() + "] spend " + (endTime - beginTime) + " ms");
                                      
          return obj;
                                      
                                  }
                              });
                  }
              }
          }

          客戶端調用改成:


          package example;

          import java.util.List;
          import example.ProxyUtil.ArrayListProxy;

          /**
           * -----------------------------------------
           * @描述  TODO
           * @作者  fancy
           * @郵箱  fancydeepin@yeah.net
           * @日期  2012-8-27 <p>
           * -----------------------------------------
           
          */
          public class ProxyApp {


              
          public static void main(String[] args){
                  
                  List
          <Integer> arrayListProxy = ArrayListProxy.PROXY.getInstance();
                  arrayListProxy.add(
          2);
                  arrayListProxy.add(
          4);
                  System.out.println(
          "--------- 迭代 ---------");
                  
          for(int i : arrayListProxy){
                      System.out.print(i 
          + "\t");
                  }
                  
              }
          }

          上面代碼中用到了枚舉 enum,如果不想用枚舉,就改用普通類來實現就行了。


            
          posted on 2012-08-27 20:43 fancydeepin 閱讀(6428) 評論(4)  編輯  收藏

          評論:
          # re: java 動態代理(Proxy) 2012-08-28 11:47 | 菠蘿大象
          我想問下,你用枚舉是為了什么?  回復  更多評論
            
          # re: java 動態代理(Proxy) 2012-08-28 19:14 | fancydeepin
          回復:@菠蘿大象

          枚舉列表的每個元素都是一個枚舉對象,它們是靜態的,枚舉的構造子默認是只能內部可用,私有的,這樣一來,防止了外部試圖自己創建對象,取得代理對象只需直接調用 getInstance() 方法而無需自己去創建對象,一般情況下,我們會提供一個靜態工廠方法,用來獲得代理對象,在這里因為目標很明確,就是要實現一個 ArrayList 的代理,所以在這里我用枚舉取代之,并不使用靜態工廠方法,或類成員,或類方法,或成員方法,用這些都能夠取得代理對象,這里的實現只是我的一點想法,如果做的不好的話,請多指教
            回復  更多評論
            
          # re: java 動態代理(Proxy) 2012-10-08 23:43 | distinys
          我想知道怎么關注你。。找不到加關注幾個字啊。。  回復  更多評論
            
          # re: java 動態代理(Proxy) 2012-10-11 15:20 | fancydeepin
          回復 @distinys

          很抱歉,我也沒找得到"關注"二字,感謝你的訪問 ^_^  回復  更多評論
            

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 尼玛县| 句容市| 兴山县| 岑巩县| 禄丰县| 贵南县| 涡阳县| 万山特区| 寿阳县| 颍上县| 富顺县| 安宁市| 廊坊市| 锡林郭勒盟| 巨鹿县| 辉南县| 集安市| 鸡泽县| 广汉市| 砀山县| 玛多县| 会同县| 庆安县| 合水县| 神农架林区| 渝北区| 大丰市| 北流市| 武邑县| 昭苏县| 滨海县| 巧家县| 星座| 巴彦淖尔市| 阿拉尔市| 界首市| 望江县| 大冶市| 信宜市| 方山县| 霍邱县|