<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    春風(fēng)博客

    春天里,百花香...

    導(dǎo)航

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

    統(tǒng)計(jì)

    公告

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

    Locations of visitors to this page

    常用鏈接

    留言簿(11)

    隨筆分類(224)

    隨筆檔案(126)

    個(gè)人軟件下載

    我的其它博客

    我的鄰居們

    最新隨筆

    搜索

    積分與排名

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    動(dòng)態(tài)代理機(jī)制初探

    功能代碼的多余枝節(jié)

    當(dāng)我們書寫執(zhí)行一個(gè)功能的函數(shù)時(shí),經(jīng)常需要在其中寫入與功能不是直接相關(guān)但很有必要的代碼,如日志記錄,信息發(fā)送,安全和事務(wù)支持等,以下代碼是一個(gè)用戶注冊(cè)類的代碼:

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

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

      
    public void register(String name,String pswd,String email){
        Logger.log(
    "將注冊(cè)一個(gè)新用戶"+name);
        
        
    // 真正的,應(yīng)該由本函數(shù)擔(dān)負(fù)的處理
        System.out.println("存儲(chǔ)用戶信息到數(shù)據(jù)庫");
        
        MailSender.send(email, 
    "歡迎"+name+"注冊(cè)為本系統(tǒng)的用戶");
      }

    }


    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);
      }

      
      
    /**
       * 取得當(dāng)前時(shí)間
       * 
    @return
       
    */

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

    }


    MailSender類代碼

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

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

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

    }



    枝節(jié)性代碼給功能性代碼帶來的麻煩

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

    如何兩種代碼分離開來

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

    Proxy代理模式

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

    消除了枝節(jié)代碼的注冊(cè)類

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

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

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

    }



    注冊(cè)類的代理類,枝節(jié)性代碼都被轉(zhuǎn)移到了這里

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

    public class RegisterServiceProxy implements InvocationHandler {
      
    // 代理對(duì)象
        Object obj;
        
        
    // 構(gòu)造函數(shù),傳入代理對(duì)象
        public RegisterServiceProxy(Object o) {
            obj 
    = o;
        }


        
    /**
         * 調(diào)用被代理對(duì)象的將要被執(zhí)行的方法,我們可以在調(diào)用之前進(jìn)行日誌記錄,之后執(zhí)行郵件發(fā)送
         
    */

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

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

            
    return result;
        }

    }


    代理類RegisterServiceProxy的解釋

    該代理類的內(nèi)部屬性為Object類,實(shí)際使用時(shí)通過該類的構(gòu)造函數(shù)RegisterServiceProxy(Object obj)對(duì)其賦值;此外,在該類還實(shí)現(xiàn)了invoke方法,該方法中的
    method.invoke(obj,args);
    其實(shí)就是調(diào)用被代理對(duì)象的將要被執(zhí)行的方法,這是通過反射實(shí)現(xiàn)的,方法參數(shù)obj是實(shí)際的被代理對(duì)象,args為執(zhí)行被代理對(duì)象相應(yīng)操作所需的參數(shù)。通過動(dòng)態(tài)代理類,我們可以在調(diào)用之前或者之后執(zhí)行一些相關(guān)操作。

    如何生成一個(gè)代理類的實(shí)例

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

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

    Proxy即為java中的動(dòng)態(tài)代理類,其方法Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理類的一個(gè)實(shí)例,其中l(wèi)oader是類加載器,interfaces是被代理的真實(shí)類的接口,h是具體的代理類實(shí)例。

    所謂動(dòng)態(tài)代理是這樣一種class:它是在運(yùn)行時(shí)生成的類,在生成它時(shí)你必須提供一組接口給它,然后該類就宣稱它實(shí)現(xiàn)了這些接口。你當(dāng)然可以把該類的實(shí)例當(dāng)作這些接口中的任何一個(gè)實(shí)現(xiàn)類來用。當(dāng)然啦,這個(gè)動(dòng)態(tài)代理類其實(shí)就是一個(gè)代理,它不會(huì)做作實(shí)質(zhì)性的工作,而是在生成它的實(shí)例時(shí)你必須提供一個(gè)真實(shí)的類的實(shí)例,由它接管實(shí)際的工作。

    工廠方法的作用

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

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

    執(zhí)行完的結(jié)果和前面的代碼的是一樣的。

    動(dòng)態(tài)代理在AOP中的應(yīng)用

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

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

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

    評(píng)論

    # re: 動(dòng)態(tài)代理機(jī)制初探 2008-05-28 11:49 隔葉黃鶯

    JDK 的動(dòng)態(tài)代理只能應(yīng)用到實(shí)現(xiàn)了接口的類,實(shí)現(xiàn)簡單的 AOP 功能,如果是完全針對(duì)接口的編程可以用這種方法。

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

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

    但要攔截一個(gè)類的方法,就得用 AspectJ 或 ASM 這樣的工具,在編譯或運(yùn)行時(shí)修改字節(jié)碼,讓這個(gè)類動(dòng)態(tài)繼承某個(gè)類或直接插入代碼到方法中。

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

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

    # re: 動(dòng)態(tài)代理機(jī)制初探 2008-05-28 12:43 如坐春風(fēng)

    @隔葉黃鶯

    謝謝提示。  回復(fù)  更多評(píng)論   

    # re: 動(dòng)態(tài)代理機(jī)制初探 2008-05-30 10:05 大衛(wèi)

    通俗易懂,學(xué)習(xí)。謝謝!  回復(fù)  更多評(píng)論   

    sitinspring(http://www.tkk7.com)原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處.
    主站蜘蛛池模板: 特黄特色的大片观看免费视频| 亚洲一区免费在线观看| 无遮挡a级毛片免费看| 国产成人在线观看免费网站| 亚洲夂夂婷婷色拍WW47| 成人毛片视频免费网站观看| 中国在线观看免费的www| a级亚洲片精品久久久久久久| japanese色国产在线看免费| 亚洲日韩激情无码一区| 国产情侣久久久久aⅴ免费| 婷婷精品国产亚洲AV麻豆不片| 999久久久免费精品播放| 亚洲大片免费观看| 暖暖免费高清日本一区二区三区| 亚洲欧洲无码一区二区三区| 国产99视频精品免费视频7| www在线观看播放免费视频日本| 亚洲日韩精品无码专区网址| 亚洲精品国产免费| 国产精品亚洲专一区二区三区| 亚洲国产成人久久笫一页| 免费a级毛片无码a∨免费软件 | 岛国岛国免费V片在线观看 | 久九九精品免费视频| 亚洲国产精品成人综合色在线| 亚洲AV无码乱码在线观看牲色| 中文字幕日本人妻久久久免费| 亚洲精品在线免费观看| 国产又大又长又粗又硬的免费视频 | 亚洲欧洲日本精品| 日韩一区二区在线免费观看 | 牛牛在线精品免费视频观看| 亚洲成A人片在线观看无码不卡| 国产在线观看麻豆91精品免费| 国产精品亚洲专区无码不卡| 亚洲av日韩av不卡在线观看| 亚洲免费综合色在线视频| 黄色短视频免费看| 亚洲国产日韩精品| 亚洲va久久久噜噜噜久久天堂|