春風博客

          春天里,百花香...

          導航

          <2008年5月>
          27282930123
          45678910
          11121314151617
          18192021222324
          25262728293031
          1234567

          統計

          公告

          MAIL: junglesong@gmail.com
          MSN: junglesong_5@hotmail.com

          Locations of visitors to this page

          常用鏈接

          留言簿(11)

          隨筆分類(224)

          隨筆檔案(126)

          個人軟件下載

          我的其它博客

          我的鄰居們

          最新隨筆

          搜索

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          動態代理機制初探

          功能代碼的多余枝節

          當我們書寫執行一個功能的函數時,經常需要在其中寫入與功能不是直接相關但很有必要的代碼,如日志記錄,信息發送,安全和事務支持等,以下代碼是一個用戶注冊類的代碼:

          /**
           * 用於用戶注冊的服務類
           * 
          @author: sitinspring(junglesong@gmail.com)
           * @date: 2008-5-27-下午09:15:25
           
          */

          public class RegisterService{
            
          /**
             * 注冊一個用戶
             * 
          @param name 用戶名
             * 
          @param pswd 用戶密碼
             * 
          @param email 用戶郵件地址
             
          */

            
          public void register(String name,String pswd,String email){
              Logger.log(
          "將注冊一個新用戶"+name);
              
              
          // 真正的,應該由本函數擔負的處理
              System.out.println("存儲用戶信息到數據庫");
              
              MailSender.send(email, 
          "歡迎"+name+"注冊為本系統的用戶");
            }

          }


          Logger類代碼

          /**
           * 模擬記錄器
           * 
          @author: sitinspring(junglesong@gmail.com)
           * @date: 2008-5-27-下午09:17:56
           
          */

          public class Logger{
            
          /**
             * 模擬記錄信息到文件中
             * 
          @param str
             
          */

            
          public static void log(String str){
              System.out.println(getCurrTime()
          +"INFO:"+str);
            }

            
            
          /**
             * 取得當前時間
             * 
          @return
             
          */

            
          private static String getCurrTime() {
              Date date 
          = new Date();
              Format formatter 
          = new SimpleDateFormat("HH時mm分ss秒");
              
          return formatter.format(date);
            }

          }


          MailSender類代碼

          /**
           * 模擬郵件發送器
           * 
          @author: sitinspring(junglesong@gmail.com)
           * @date: 2008-5-27-下午09:23:31
           
          */

          public class MailSender{
            
          /**
             * 模擬發送郵件
             * 
          @param title
             * 
          @param msg
             
          */

            
          public static void send(String email,String concept){
              System.out.println(
          ""+email+"發送郵件 內容為:"+concept+"的郵件");
            }

          }



          枝節性代碼給功能性代碼帶來的麻煩

          諸如日志記錄,信息發送,安全和事務支持等枝節代碼雖然是必要的,但它會帶來以下麻煩:
          1.枝節性代碼游離在功能性代碼之外,它們不是函數的目的,這對OO是一種破壞。
          2.枝節性代碼會造成功能性代碼對其它類的依賴,加深類之間的耦合度,而這是OO系統所竭力避免的。
          3.枝節性代碼帶來的耦合度會造成功能性代碼移植困難,可重用性降低。
          4.從法理上說,枝節性代碼應該“監視”著功能性代碼,然后采取行動;而不是由功能性代碼“通知”枝節性代碼采取行動。這好比吟游詩人應該是主動記述騎士的功績而不是騎士主動要求詩人記錄自己的功績的。

          如何兩種代碼分離開來

          毫無疑問,枝節性代碼和功能性代碼(主干性代碼)需要分離開來才能降低耦合程度,符合現代OO系統的要求,而java提供的動態代理機制可以幫助我們實現這一點。
          動態代理機制主要的類是java.lang.reflect.Proxy,它從一誕生就受到了重視,并在RMI,EJB和AOP中都得到廣泛的應用,其重要程度唯有反射能與之相比。

          Proxy代理模式

          在講述動態代理之前我們可以回顧一下代理模式,它的定義是這樣的:代理可以提供對另一個對象的訪問,同時隱藏實際對象的具體事實。代理一般會實現它所表示的實際對象的接口。代理可以訪問實際對象,但是延遲實現實際對象的部分功能,實際對象實現系統的實際功能,代理對象對客戶隱藏了實際對象。客戶不知道它是與代理打交道還是與實際對象打交道。
          如果我們使用代理模式,把枝節性代碼放入代理類中,這樣主干性代碼保持在真實的類中,這樣不就能有效降低耦合度嗎?這種通過在耦合緊密的類之間引入一個中間類是降低類之間的耦合度的常見做法。
          具體來說就是把枝節性代碼放入代理類中,它們由代理類負責調用,而真實類只負責主干的核心業務,它也由代理類調用,它并不知道枝節性代碼的存在和作用,因為這本不是它的任務。對外來說,代理類隱藏在接口之后,客戶并不清楚也不需要清楚具體的調用過程。通過這樣的處理,主干與枝節之間的交叉解開了,外界的調用也沒有復雜化,這就有效降低系統各部分間的耦合度。
          下面讓我們先看看代碼

          消除了枝節代碼的注冊類

          /**
           * 用於用戶注冊的服務類
           * 
          @author: sitinspring(junglesong@gmail.com)
           * @date: 2008-5-27-下午09:15:25
           
          */

          public class RegisterService implements IService{
            
          /**
             * 注冊一個用戶
             * 
          @param name 用戶名
             * 
          @param pswd 用戶密碼
             * 
          @param email 用戶郵件地址
             
          */

            
          public void register(String name,String pswd,String email){
              
          // 真正的,該由本函數擔負的處理
              System.out.println("存儲用戶信息到數據庫");
            }

          }



          注冊類的代理類,枝節性代碼都被轉移到了這里

          /**
           * 注冊服務代理類
           * 
          @author: sitinspring(junglesong@gmail.com)
           * @date: 2008-5-27-下午09:45:10
           
          */

          public class RegisterServiceProxy implements InvocationHandler {
            
          // 代理對象
              Object obj;
              
              
          // 構造函數,傳入代理對象
              public RegisterServiceProxy(Object o) {
                  obj 
          = o;
              }


              
          /**
               * 調用被代理對象的將要被執行的方法,我們可以在調用之前進行日誌記錄,之后執行郵件發送
               
          */

              
          public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
                  Object result 
          = null;
                  
          try {
                    
          // 進行日誌記錄
                      String name=(String)args[0];
                    Logger.log(
          "將注冊一個新用戶"+name);

                    
          // 調用Object的方法
                      result = m.invoke(obj, args);
                      
                      
          // 執行郵件發送
                      String email=(String)args[2];
                      MailSender.send(email, 
          "歡迎"+name+"注冊為本系統的用戶");
                  }
           catch (InvocationTargetException e) {
                  }
           catch (Exception eBj) {
                  }
           finally {
                      
          // Do something after the method is called 
                  }

                  
          return result;
              }

          }


          代理類RegisterServiceProxy的解釋

          該代理類的內部屬性為Object類,實際使用時通過該類的構造函數RegisterServiceProxy(Object obj)對其賦值;此外,在該類還實現了invoke方法,該方法中的
          method.invoke(obj,args);
          其實就是調用被代理對象的將要被執行的方法,這是通過反射實現的,方法參數obj是實際的被代理對象,args為執行被代理對象相應操作所需的參數。通過動態代理類,我們可以在調用之前或者之后執行一些相關操作。

          如何生成一個代理類的實例

          代理類的實例需要特殊的方式生成,代碼如下:

            public static IService genereteService(){
              
          return (IService)Proxy.newProxyInstance(
                  IService.
          class.getClassLoader(),
                      
          new Class[]{IService.class},
                      
          new RegisterServiceProxy(new RegisterService()));
            }

          Proxy即為java中的動態代理類,其方法Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理類的一個實例,其中loader是類加載器,interfaces是被代理的真實類的接口,h是具體的代理類實例。

          所謂動態代理是這樣一種class:它是在運行時生成的類,在生成它時你必須提供一組接口給它,然后該類就宣稱它實現了這些接口。你當然可以把該類的實例當作這些接口中的任何一個實現類來用。當然啦,這個動態代理類其實就是一個代理,它不會做作實質性的工作,而是在生成它的實例時你必須提供一個真實的類的實例,由它接管實際的工作。

          工廠方法的作用

          對于代理類生成的細節,客戶(需要使用RegisterService的程序員)是沒有興趣也沒有必要知道的,我們可以讓它隱藏在一個工廠方法中,對外返回一個接口,這樣在調用時用戶就不知道他是與代理打交道還是與實際對象打交道了。使用RegisterService類時示例代碼如下:

          IService service=RegisterServiceFactory.genereteService();    
          service.register(
          "sitinspring","123456","junglesong@gmail.com");

          執行完的結果和前面的代碼的是一樣的。

          動態代理在AOP中的應用

          Spring的AOP支持可以被用于從系統核心邏輯中分離交叉業務(cross-business)如日志,事務管理和安全等,使用AOP,你可以用各種功能層來覆蓋核心業務層,這些功能層可以靈活的應用到你的系統中,甚至核心業務層都不知道它們的存在,這是一個強大的概念。
          AOP(aspect-oriented programming)的核心就是動態代理,掌握它對于理解AOP尤為重要,猶如反射對理解IoC一樣。

          代碼下載:
          http://www.aygfsteel.com/Files/sitinspring/DynamicProxySample20080527235441.rar

          posted on 2008-05-28 00:06 sitinspring 閱讀(2374) 評論(3)  編輯  收藏 所屬分類: SSH

          評論

          # re: 動態代理機制初探 2008-05-28 11:49 隔葉黃鶯

          JDK 的動態代理只能應用到實現了接口的類,實現簡單的 AOP 功能,如果是完全針對接口的編程可以用這種方法。

          關鍵在
          return (IService)Proxy.newProxyInstance(
          IService.class.getClassLoader(),
          new Class[]{IService.class},
          new RegisterServiceProxy(new RegisterService()));
          用這種方式獲取 RegisterService 實例時,JDK 動態代理給你駁接了代碼。

          如果你用 new RegisterService() 獲取實例,仍然是無能為力了。

          但要攔截一個類的方法,就得用 AspectJ 或 ASM 這樣的工具,在編譯或運行時修改字節碼,讓這個類動態繼承某個類或直接插入代碼到方法中。

          如果是修改字節碼的方式,可以直接攔截到構造方法,讓你無論何時以何種方式得到的 RegisterService 的實例,在執行 register() 方法時都能夠打出日志,發出郵件來。

          AspectJ 是一個很好的工具,好像直接用它,寫 *.aj 文件的比較少,主要它自己有一套文法,很多人不適應。不過在 Spring 2.0 開始可以在 context.xml 文件里應用 aspectj 了。  回復  更多評論   

          # re: 動態代理機制初探 2008-05-28 12:43 如坐春風

          @隔葉黃鶯

          謝謝提示。  回復  更多評論   

          # re: 動態代理機制初探 2008-05-30 10:05 大衛

          通俗易懂,學習。謝謝!  回復  更多評論   

          sitinspring(http://www.aygfsteel.com)原創,轉載請注明出處.
          主站蜘蛛池模板: 衡南县| 临沧市| 齐河县| 济源市| 遂平县| 红桥区| 徐州市| 方城县| 油尖旺区| 云安县| 紫金县| 万州区| 海口市| 西吉县| 疏勒县| 中江县| 宜州市| 宾阳县| 夏邑县| 庆云县| 丹东市| 碌曲县| 利川市| 石楼县| 资兴市| 萍乡市| 凤山市| 九龙坡区| 扎鲁特旗| 淮安市| 连州市| 灵丘县| 曲靖市| 义乌市| 长寿区| 岫岩| 蒙城县| 德兴市| 方城县| 青岛市| 武宁县|