qileilove

          blog已經(jīng)轉移至github,大家請訪問 http://qaseven.github.io/

          代理和AOP

          代理和AOP一.起源
          有時,我們在寫一些功能方法的時候,需要加上特定的功能.比如說在方法調用的前后加上日志的操作,或者是事務的開啟與關閉.對于一個方法來說,很簡單,只要在需要的地方增加一些代碼就OK.但是如果有很多方法都需要增加這種特定的操作呢?

          沒錯,將這些特定的代碼抽象出來,并且提供一個接口供調用者使用:
          Java代碼 復制代碼 收藏代碼
          1. public class RecordLog   
          2. {   
          3.     public static void recordLog()   
          4.     {   
          5.         //  記錄日志的操作   
          6.         System.out.println("記錄日志...");   
          7.     }   
          8. }  

          那么在其他的方法中,就可以使用RecordLog.recordLog()方法了.但你會發(fā)現(xiàn),這仍不是個好的設計,因為在我們的代碼里到處充塞著
          RecordLog.recordLog()這樣的語句:
          Java代碼 復制代碼 收藏代碼
          1. public class A   
          2. {   
          3.     public void a()   
          4.     {   
          5.         //  1.記錄日志   
          6.         RecordLog.recordLog();   
          7.   
          8.         //  2.類A的方法a的操作   
          9.     }   
          10. }   
          11. public class B   
          12. {   
          13.     public void b()   
          14.     {   
          15.         //  1.記錄日志   
          16.         RecordLog.recordLog();   
          17.   
          18.         //  2.類B的方法b的操作   
          19.     }   
          20. }   
          21. ......  

          這樣雖然會在一定程度減輕代碼量,但你會發(fā)現(xiàn),仍有大量的地方有重復的代碼出現(xiàn)!這絕對不是優(yōu)雅的寫法!

          為了避免這種吃力不討好的現(xiàn)象發(fā)生,“代理”粉墨登場了.

          二.傳統(tǒng)的代理.靜態(tài)的代理.面向接口編程
          同樣為了實現(xiàn)以上的功能,我們在設計的時候做了個小小的改動.

          2.1 抽象出來的記錄日志的類:
          Java代碼 復制代碼 收藏代碼
          1. public class RecordLog   
          2. {   
          3.     public static void recordLog()   
          4.     {   
          5.         //  記錄日志的操作   
          6.         System.out.println("記錄日志...");   
          7.     }   
          8. }  

          2.2 設計了一個接口:
          Java代碼 復制代碼 收藏代碼
          1. public interface PeopleInfo   
          2. {   
          3.     public void getInfo();   
          4. }  

          該接口只提供了待實現(xiàn)的方法.

          2.3 實現(xiàn)該接口的類:
          Java代碼 復制代碼 收藏代碼
          1. public class PeopleInfoImpl implements PeopleInfo   
          2. {      
          3.     private String name;   
          4.   
          5.     private int age;   
          6.   
          7.     //  構造函數(shù)   
          8.     public PeopleInfoImpl(String name, int age)   
          9.     {   
          10.         this.name = name;   
          11.         this.age  = age;   
          12.     }   
          13.   
          14.     public void getInfo()   
          15.     {   
          16.         //  方法的具體實現(xiàn)   
          17.         System.out.println("我是" + name + ",今年" + age + "歲了.");   
          18.     }   
          19. }  

          這個類僅僅是實現(xiàn)了PeopleInfo接口而已.平平實實.好了.關鍵的地方來了.就在下面!

          2.4 創(chuàng)建一個代理類:
          Java代碼 復制代碼 收藏代碼
          1. public class PeopleInfoProxy implements PeopleInfo   
          2. {   
          3.     //  接口的引用   
          4.     private PeopleInfo peopleInfo;   
          5.   
          6.     //  構造函數(shù)    .針對接口編程,而非針對具體類   
          7.     public RecordLogProxy(PeopleInfo peopleInfo)   
          8.     {   
          9.         this.peopleInfo = peopleInfo;   
          10.     }   
          11.   
          12.     //  實現(xiàn)接口中的方法   
          13.     public void record()   
          14.     {   
          15.         //  1.記錄日志   
          16.         RecordLog.recordLog();   
          17.   
          18.         //  2.方法的具體實現(xiàn)   
          19.         peopleInfo.getInfo();   
          20.     }   
          21. }  

          這個是類是一個代理類,它同樣實現(xiàn)了PeopleInfo接口.比較特殊的地方在于這個類中有一個接口的引用private PeopleInfo peopleInfo;通過
          這個引用,可以調用實現(xiàn)了該接口的類的實例的方法!
          而不管是誰,只要實現(xiàn)了PeopleInfo這個接口,都可以被這個引用所引用.也就是說,這個代理類可以代理任何實現(xiàn)了接口的PeopleInfo的類.具體
          如何實現(xiàn),請看下面:

          2.5 Main
          Java代碼 復制代碼 收藏代碼
          1. public class Main   
          2. {   
          3.     public static void main(String[] args)   
          4.     {   
          5.         //  new了一個對象   
          6.         PeopleInfoImpl peopleInfoImpl = new PeopleInfoImpl("Rock",24);   
          7.   
          8.         //  代理該對象   
          9.         PeopleInfoProxy peopleInfoProxy = new PeopleInfoProxy(PeopleInfoImpl);   
          10.   
          11.         //  調用代理類的方法.輸入的是目標類(即被代理類的方法的實現(xiàn))   
          12.         peopleInfoProxy.getInfo();   
          13.     }   
          14. }  

          這樣,輸出的結果將是:
          記錄日志...
          我是Rock,今年24歲了.

          由這個例子可見,這么做了之后不但省略了很多代碼,而且不必要知道具體是由哪個類來執(zhí)行方法.只需實現(xiàn)了特定的接口,代理類就可以打點一切
          了.這就是面向接口的威力!HOHO...

          三.動態(tài)代理.Java的動態(tài)機制.
          面向接口的編程確實讓我們省了不少心,只要實現(xiàn)一個特定的接口,就可以處理很多的相關的類了.
          不過,這總是要實現(xiàn)一個“特定”的接口,如果有很多很多這樣的接口需要被實現(xiàn)...也是件比較麻煩的事情.

          好在,JDK1.3起,就有了動態(tài)代理機制,主要有以下兩個類和一個接口:
          Java代碼 復制代碼 收藏代碼
          1. java.lang.reflect.Proxy   
          2. java.lang.reflect.Method   
          3. java.lang.reflect.InvocationHandler  

          所謂動態(tài)代理,就是JVM在內存中動態(tài)的構造代理類.說的真是玄,還是看看代碼吧.

          3.1 抽象出來的記錄日志的類:
          Java代碼 復制代碼 收藏代碼
          1. public class RecordLog   
          2. {   
          3.     public static void recordLog()   
          4.     {   
          5.         //  記錄日志的操作   
          6.         System.out.println("記錄日志...");   
          7.     }   
          8. }  

          3.2 設計了一個接口:
          Java代碼 復制代碼 收藏代碼
          1. public interface PeopleInfo   
          2. {   
          3.     public void getInfo();   
          4. }  

          該接口只提供了待實現(xiàn)的方法.

          3.3 實現(xiàn)該接口的類:
          Java代碼 復制代碼 收藏代碼
          1. public class PeopleInfoImpl implements PeopleInfo   
          2. {      
          3.     private String name;   
          4.   
          5.     private int age;   
          6.   
          7.     //  構造函數(shù)   
          8.     public PeopleInfoImpl(String name, int age)   
          9.     {   
          10.         this.name = name;   
          11.         this.age  = age;   
          12.     }   
          13.   
          14.     public void getInfo()   
          15.     {   
          16.         //  方法的具體實現(xiàn)   
          17.         System.out.println("我是" + name + ",今年" + age + "歲了.");   
          18.     }   
          19. }  

          一直到這里,都和第二節(jié)沒區(qū)別,好嘛,下面就是關鍵喲.

          3.4 創(chuàng)建一個代理類,實現(xiàn)了接口InvocationHandler:
          Java代碼 復制代碼 收藏代碼
          1. public class PeopleInfoProxy implements InvocationHandler   
          2. {   
          3.     //  定義需要被代理的目標對象   
          4.     private Object target;   
          5.   
          6.     //  將目標對象與代理對象綁定   
          7.     public Object bind(Object targer)   
          8.     {   
          9.         this.target = target;   
          10.   
          11.         //  調用Proxy的newProxyInstance方法產(chǎn)生代理類實例   
          12.         return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);   
          13.     }   
          14.   
          15.     //  實現(xiàn)接口InvocationHandler的invoke方法   
          16.     //  該方法將在目標類的被代理方法被調用之前,自動觸發(fā)   
          17.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable   
          18.     {   
          19.         Object result = null;   
          20.            
          21.         //  1.目標類的被代理方法被調用之前,可以做的操作   
          22.         RecordLog.recordLog();   
          23.   
          24.         //  2.方法的具體實現(xiàn)   
          25.         result = method.invoke(target, args);   
          26.   
          27.         //  3.還可以在方法調用之后加上的操作   
          28.         //  自己補充   
          29.   
          30.         return result;   
          31.     }   
          32. }  

          關于Proxy, Method, InvocationHandler的具體說明,請參見JDK_API.

          只對代碼中關鍵部分做些解釋說明:
          3.4.1
          Java代碼 復制代碼 收藏代碼
          1. Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);   

          表示生成目標類的代理類,傳入的參數(shù)有目標類的ClassLoader, 目標類的接口列表, 和實現(xiàn)了接口InvocationHandler的代理類.
          這樣,bind方法就得到了目標類的代理類.

          3.4.2
          Java代碼 復制代碼 收藏代碼
          1. method.invoke(target, args);  

          目標類的被代理方法在被代用前,會自動調用InvocationHandler接口的invoke方法.
          在該方法中,我們可以對目標類的被代理方法進行加強,比如說在其前后加上事務的開啟和關閉等等.
          這段代碼才是真正調用目標類的被代理方法.

          就這樣,我們不用實現(xiàn)其他任何的接口,理論上就能代理所有類了.調用的方式如下:

          3.5 Main:
          Java代碼 復制代碼 收藏代碼
          1. public class Main   
          2. {   
          3.     public static void main(String[] args)   
          4.     {   
          5.         PeopleInfo peopleInfo = null;   
          6.   
          7.         PeopleInfoProxy peopleInfoProxy = new PeopleInfoProxy();   
          8.   
          9.         //  傳入的參數(shù)是目標類實例,生成代理類實例,類型為Object   
          10.         Object obj = peopleInfoProxy.bind(new PeopleInfoImpl("Rock"24));   
          11.   
          12.         if(obj instanceof PeopleInfo)   
          13.         {   
          14.             peopleInfo = (PeopleInfo)obj;   
          15.         }   
          16.         peopleInfo.getInfo();   
          17.     }   
          18. }  

          執(zhí)行結果和上一節(jié)一樣.
          這就是使用Java動態(tài)代理機制的基本概述.而下一節(jié),將要把Dynamic Proxy(動態(tài)代理)和AOP聯(lián)系起來.

          四.AOP概述.Spring的AOP.
          AOP(Aspect Oriented Programming)面向切面編程.是一種比較新穎的設計思想.是對OOP(Object Orientd Programming)面向對象編程的一種有益的補充.

          4.1 OOP和AOP
          OOP對業(yè)務處理過程中的實體及其屬性和行為進行了抽象封裝,以獲得更加清晰高效果的邏輯劃分.研究的是一種“靜態(tài)的”領域.
          AOP則是針對業(yè)務處理過程中的切面進行提取,它所面對的是處理過程中的某個步驟或階段.研究的是一種“動態(tài)的”領域.

          舉例說,某個網(wǎng)站(5016?)用戶User類又可分為好幾種,區(qū)長,管理員,斑竹和普通水友.我們把這些會員的特性進行提取進行封裝,這是OOP.
          而某一天,區(qū)長開會了,召集斑竹等級以上的會員參與,這樣,普通水友就不能訪問相關資源.

          我們怎么做到讓普通水友訪問不了資源,而斑竹等級以上會員可以訪問呢.
          權限控制.對,權限.當水友們進行操作的時候,我們給他的身份進行權限的判斷.
          請注意,當且僅需水友門執(zhí)行了操作的時候,我們才需要進行權限判斷,也就是說,這是發(fā)生在一個業(yè)務處理的過程中的一個片面.
          我們對這一個片面進行編程,就是AOP!

          我這樣,你應該能理解吧.

          4.2 AOP的基本術語
          4.2.1 切面Aspect
          業(yè)務處理過程中的一個截面.就像權限檢查.
          通過切面,可以將不同層面的問題隔離開:瀏覽帖子和權限檢查兩者互不相干.
          這樣一來,也就降低了耦合性,我們可以把注意力集中到各自的領域中.
          上兩節(jié)的例子中,getInfo()和recordLog()就是兩個領域的方法,應該處于切面的不同端.哎呀,不知不覺間,我們就用了AOP.呵呵...

          4.2.2 連接點JoinPoint
          程序運行中的某個階段點.如某個方法的調用,或者異常的拋出等.
          在前面,我們總是在getInfo()的前后加了recordLog()等操作,這個調用getInfo()就是連接點.

          4.2.3 處理邏輯Advice
          在某個連接點采取的邏輯.
          這里的邏輯有三種:
          I. Around 在連接點前后插入預處理和后處理過程.
          II. Before 在連接點前插入預處理過程.
          III.Throw 在連接點拋出異常的時候進行異常處理.

          4.2.4 切點PointCut
          一系列連接點的集合,它指明處理邏輯Advice將在何在被觸發(fā).

          4.3 Spring中的AOP
          Spring提供內置AOP支持.是基于動態(tài)AOP機制的實現(xiàn).
          所謂動態(tài)AOP,其實就是動態(tài)Proxy模式,在目標對象的方法前后插入相應的代碼.(比如說在getInfo()前后插入的recordLog())
          Spring AOP中的動態(tài)Proxy模式,是基于Java Dynamic Proxy(面向Interface)和CGLib(面向Class)的實現(xiàn).

          為什么要分面向接口和面向類呢.
          還記得我們在生成代理類的代碼嗎:
          Java代碼 復制代碼 收藏代碼
          1. Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);  

          這里面的參數(shù)不許為空,也就是說:obj.getClass().getInterfaces()必有值,即目標類一定要實現(xiàn)某個接口.

          有了這些,JVM在內存中就動態(tài)的構造出代理出來.

          而沒有實現(xiàn)任何接口的類,就必須使用CGLib來動態(tài)構造代理類.值得一提的是,CGLib構造的代理類是目標類的一個子類.

          4.4 相關工程簡解
          Spring的相關知識不應該在這里講,難度系數(shù)過大.這里只給個簡單例子.供參考.
          4.4.1 準備工作
          打開Eclipse.新建Java工程,取名為AOP_Proxy.完成.
          復制spring-2.0.jar.粘貼到AOP_Proxy下.
          右擊AOP_Proxy-->屬性-->Java構建路徑-->庫-->添加JAR-->找spring-2.0.jar-->添加確定.
          復制commons-logging.jar.粘貼到AOP_Proxy下.
          右擊AOP_Proxy-->屬性-->Java構建路徑-->庫-->添加JAR-->找commons-logging.jar-->添加確定.

          4.4.2 寫代碼
          代碼略.配置文件略.
                   4.4.3 導入工程的步驟
          新建工程AOP_Pro/Files/qileilove/AOP_Proxy.rarxy-->完成-->右擊AOP_Proxy-->導入-->常規(guī)-->文件系統(tǒng)-->找到項目文件,導入完成.

          兩個jar包和項目文件(項目文件需要先解壓).



          posted on 2011-09-22 16:49 順其自然EVO 閱讀(511) 評論(0)  編輯  收藏

          <2011年9月>
          28293031123
          45678910
          11121314151617
          18192021222324
          2526272829301
          2345678

          導航

          統(tǒng)計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 洱源县| 灯塔市| 从化市| 桃园市| 汉寿县| 朔州市| 吉水县| 南岸区| 南安市| 夹江县| 大冶市| 中方县| 张北县| 新疆| 清苑县| 鸡泽县| 连城县| 获嘉县| 松阳县| 辽阳市| 泾川县| 岗巴县| 白河县| 渝北区| 丹阳市| 岚皋县| 竹溪县| 盖州市| 安乡县| 乌兰浩特市| 郎溪县| 澄江县| 庆安县| 黄冈市| 基隆市| 天台县| 万安县| 南康市| 南充市| 怀化市| 雅安市|