posts - 495,  comments - 11,  trackbacks - 0

          6.2 Spring的AOP

          AOP(Aspect Orient Programming),也就是面向切面編程,作為面向對象編程的一種補充。問世的時間并不太長,甚至在國內的翻譯還不太統一(有些書翻譯成面向方面編程),但它確實極好地補充了面向對象編程的方式。面向對象編程將程序分解成各個層次的對象,而面向切面編程將程序運行過程分解成各個切面。

          可以這樣理解,面向對象編程是從靜態角度考慮程序結構,面向切面編程是從動態角度考慮程序運行過程。

          Spring AOP是Spring框架的一個重要組件,極好地補充了Spring IoC容器的功能。Spring AOP將Spring IoC容器與AOP組件緊密結合,豐富了IoC容器的功能。當然,即使不使用AOP組件,依然可以使用Spring的IoC容器。

          6.2.1 AOP的基本概念

          AOP從程序運行角度考慮程序的流程,提取業務處理過程的切面。AOP面向的是程序運行中各個步驟,希望以更好的方式來組合業務處理的各個步驟。

          AOP框架并不與特定的代碼耦合,AOP框架能處理程序執行中的特定點,而不是某個具體的程序。AOP框架具有如下兩個特征:

          ?? ● 各步驟之間的良好隔離性。

          ?? ● 源代碼無關性。

          下面是關于面向切面編程的一些術語:

          ?? ● 切面,業務流程運行的某個特定步驟,就是運行過程的關注點,關注點可能橫切多個對象。

          ?? ● 連接點,程序執行過程中明確的點,如方法的調用或異常的拋出。Spring AOP中,連接點總是方法的調用,Spring并沒有顯式地使用連接點。

          ?? ● 處理(Advice),AOP框架在特定的連接點執行的動作。處理有around、before和throws等類型。大部分框架都以攔截器作為處理模型。

          ?? ● 切入點,系列連接點的集合,它確定處理觸發的時機。AOP框架允許開發者自己定義切入點,如使用正則表達式。

          ?? ● 引入,添加方法或字段到被處理的類。Spring允許引入新的接口到任何被處理的對象。例如,可以使用一個引入,使任何對象實現IsModified接口,以此來簡化緩存。

          ?? ● 目標對象,包含連接點的對象。也稱為被處理對象或被代理對象。

          ?? ● AOP代理,AOP框架創建的對象,包含處理。簡單地說,代理就是對目標對象的加強。Spring中的AOP代理可以是JDK動態代理,也可以是CGLIB代理。前者為實現接口的目標對象的代理,后者為不實現接口的目標對象的代理。

          注意:面向切面編程是比較前沿的知識,而國內大部分翻譯人士翻譯計算機文獻時,總是一邊開著各種詞典和翻譯軟件,一邊逐詞去看文獻,不是先從總體上把握知識的架構。因此,難免導致一些術語的翻譯詞不達意,例如,Socket被翻譯成“套接字”等。在面向切面編程的各術語翻譯上,也存在較大的差異。對于Advice一詞,有翻譯為“通知”的,有翻譯為“建議”的,如此種種,不一而足。實際上,Advice指AOP框架在特定切面所做的事情,故而筆者翻譯為“處理”,希望可以表達Advice的真正含義。

          6.2.2 AOP的代理

          所謂AOP代理,就是AOP框架動態創建的對象,這個對象通常可以作為目標對象的替代品,而AOP代理提供比目標對象更加強大的功能。真實的情形是,當應用調用AOP代理的方法時,AOP代理會在自己的方法中回調目標對象的方法,從而完成應用的調用。

          關于AOP代理的典型例子就是Spring中的事務代理Bean。通常,目標Bean的方法不是事務性的,而AOP代理包含目標Bean的全部方法,而且這些方法經過加強變成了事務性方法。簡單地說,目標對象是藍本,AOP代理是目標對象的加強,在目標對象的基礎上,增加屬性和方法,提供更強大的功能。

          目標對象包含一系列切入點。切入點可以觸發處理連接點集合。用戶可以自己定義切入點,如使用正則表達式。AOP代理包裝目標對象,在切入點處加入處理。在切入點加入的處理,使得目標對象的方法功能更強。

          Spring默認使用JDK動態代理實現AOP代理,主要用于代理接口。也可以使用CGLIB代理。實現類的代理,而不是接口。如果業務對象沒有實現接口,默認使用CGLIB代理。但面向接口編程是良好的習慣,盡量不要面向具體類編程。因此,業務對象通常應實現一個或多個接口。

          下面是一個簡單動態代理模式的示例,首先有一個Dog的接口,接口如下:

          public interface Dog

          {

          ??? //info方法聲明

          ??? public void info();

          ??? //run方法聲明

          ??? public void run();

          }

          然后,給出該接口的實現類,實現類必須實現兩個方法,源代碼如下:

          public class DogImpl implements Dog

          {

          ??? //info方法實現,僅僅打印一個字符串

          ??? public void info()

          ??? {

          ??????? System.out.println("我是一只獵狗");

          ??? }

          ??? //run方法實現,僅僅打印一個字符串

          ??? public void run()

          ??? {

          ??????? System.out.println("我奔跑迅速");

          ??? }

          }

          上面的代碼沒有絲毫獨特之處,是典型的面向接口編程的模型,為了有更好的解耦,采用工廠來創建Dog實例。工廠源代碼如下:

          public class DogFactory

          {

          ??? //工廠本身是單態模式,因此,將DogFactory作為靜態成員變量保存

          ??? private static DogFactory df;

          ??? //將Dog實例緩存

          ??? private Dog gundog;

          ??? //默認的構造器,單態模式需要的構造器是private

          ??? private DogFactory()

          ??? {

          ??? }

          ??? //單態模式所需的靜態方法,該方法是創建本類實例的唯一方法點

          ??? public static DogFactory instance()

          ??? {

          ??????? if (df == null)

          ??????? {

          ??????????? df = new DogFactory();

          ??????? }

          ??????? return df;

          ??? }

          ??? //獲得Dog實例

          ??? public Dog getDog(String dogName)

          ??? {

          ??????? //根據字符串參數決定返回的實例

          ??????? if (dogName.equals("gundog"))

          ??????? {

          ??????????? //返回Dog實例之前,先判斷緩存的Dog是否存在,如果不存在才創建,

          ??????????? 否則直接返回緩存的Dog實例

          ??????????? if (gundog == null )

          ??????????? {

          ??????????????? gundog = new DogImpl();

          ??????????? }

          ??????????? return gundog;

          ??? ???? }

          ??????? return null;

          ??? }

          }

          下面是一個通用的處理類,該處理類沒有與任何特定的類耦合,它可以處理所有的目標對象。從JDK 1.3起,Java的import java.lang.reflect下增加InvocationHandler接口,該接口是所有處理類的根接口。

          該類處理類的源代碼如下:

          public class ProxyHandler implements InvocationHandler

          {

          ??? //需被代理的目標對象

          ??? private Object target;

          ??? //執行代理的目標方法時,該invoke方法會被自動調用

          ??? public Object invoke(Object proxy, Method method, Object[] args)throws
          ??? Exception

          ??? {

          ??????? Object result = null;

          ??????? if (method.getName().equals("info"))

          ??????? {

          ??????????? System.out.println("======開始事務...");

          ??????????? result =method.invoke(target, args);

          ??????????? System.out.println("======提交事務...");

          ??????? }

          ??????? else

          ??????? {

          ??????????? result =method.invoke(target, args);

          ??????? }

          ??????? return result;

          ??? }

          ??? //通過該方法,設置目標對象

          ??? public void setTarget(Object o)

          ??? {

          ??????? this.target = o;

          ??? }

          }

          該處理類實現InvocationHandler接口,實現該接口必須實現invoke(Object proxy, Method method, Object[] args)方法,程序調用代理的目標方法時,自動變成調用invoke方法。

          該處理類并未與任何接口或類耦合,它完全是通用的,它的目標實例是Object類型,可以是任何的類型。

          在invoke方法內,對目標對象的info方法進行簡單加強,在開始執行目標對象的方法之前,先打印開始事務,執行目標對象的方法之后,打印提交事務。

          通過method對象的invoke方法,可以完成目標對象的方法調用,執行代碼如下:

          result =method.invoke(target, args);

          下面是代理工廠:

          public class MyProxyFactory

          {

          ??? /**

          ????? * 實例Service對象

          ???? * @param serviceName String

          ???? * @return Object

          ???? */

          ??? public static Object getProxy(Object object)

          ??? {

          ?????? //代理的處理類

          ??????? ProxyHandler handler = new ProxyHandler();

          ?????? //把該dog實例托付給代理操作

          ??????? handler.setTarget(object);

          ??????? //第一個參數是用來創建動態代理的ClassLoader對象,只要該對象能訪問Dog接口
          ??????? 即可

          ??????? //第二個參數是接口數組,正是代理該接口數組

          ??????? //第三個參數是代理包含的處理實例

          ??????? return Proxy.newProxyInstance(DogImpl.class.getClassLoader(),

          ??????????? object.getClass().getInterfaces(),handler);

          ??? }

          }

          代理工廠里有一行代碼:

          Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),handler);

          Proxy.newProxyInstance()方法根據接口數組動態創建代理類實例,接口數組通過object.getClass().getInterfaces()方法獲得,創建的代理類是JVM在內存中動態創建的,該類實現傳入接口數組的全部接口。

          因此,Dynamic Proxy要求被代理的必須是接口的實現類,否則無法為其構造相應的動態類。因此,Spring對接口實現類采用Dynamic Proxy實現AOP,而對沒有實現任何接口的類,則通過CGLIB實現AOP代理。

          下面是主程序:

          public class TestDog

          {

          ??? public static void main(String[] args)

          ??? {

          ??????? Dog dog = null;

          ??????? //創建Dog實例,該實例將作為被代理對象

          ??????? Dog targetObject = DogFactory.instance().getDog("gundog");

          ??????? //以目標對象創建代理

          ??????? Object proxy = MyProxyFactory.getProxy(targetObject);

          ??????? if (proxy instanceof Dog)

          ??????? {

          ??????????? dog = (Dog)proxy;

          ??????? }

          ??????? //測試代理的方法

          ??????? dog.info();

          ??????? dog.run();

          ??? }

          }

          代理實例會實現目標對象實現的全部接口。因此,代理實例也實現了Dog接口,程序運行結果如下:

          [java] ======開始事務...

          [java] 我是一只獵狗

          [java] ======提交事務...

          [java] 我奔跑迅速

          代理實例加強了目標對象的方法——僅僅打印了兩行字符串。當然,此種加強沒有實際意義。試想一下,若程序中打印字符串的地方,換成真實的事務開始和事務提交,則代理實例的方法為目標對象的方法增加了事務性。

          6.2.3 創建AOP代理

          通過前面的介紹,AOP代理就是由AOP框架動態生成的一個對象,該對象可作為目標對象使用,AOP代理包含了目標對象的全部方法。但AOP代理中的方法與目標對象的方法存在差異:AOP方法在特定切面插入處理,在處理之間回調目標對象的方法。

          AOP代理所包含的方法與目標對象所包含的方法的示意圖,如圖6.1所示。

          文本框:圖6.1  AOP代理的方法與目標對象的方法Spring中AOP代理由Spring的IoC容器負責生成和管理,其依賴關系也由IoC容器負責管理。因此,AOP代理能夠引用容器中的其他Bean實例,這種引用由IoC容器的依賴注入提供。

          Spring的AOP代理大都由ProxyFactoryBean工廠類產生,如圖6.1所示,產生一個AOP代理至少有兩個部分:目標對象和AOP框架所加入的處理。因此,配置ProxyFactoryBean時需要確定如下兩個屬性:

          ?? ● 代理的目標對象。

          ?? ● 處理(Advice)。

          注意:關于Advice的更多知識,此處由于篇幅原因,無法深入討論。實際上,Spring也提供了很多種Advice的實現,如需要更深入了解Spring AOP,請讀者參考筆者所著的《Spring2.0寶典》。

          所有代理工廠類的父類是org.springframework.aop.framework.ProxyConfig。因此,該類的屬性是所有代理工廠的共同屬性,這些屬性也是非常關鍵的,包括:

          ?? ● proxyTargetClass,確定是否代理目標類,如果需要代理目標是類,該屬性設為true,此時需要使用CGLIB生成代理;如果代理目標是接口,該屬性設為false,默認是false。

          ?? ● optimize,確定是否使用強優化來創建代理。該屬性僅對CGLIB代理有效;對JDK 動態代理無效。

          ?? ● frozen,確定是否禁止改變處理,默認是false。

          ?? ● exposeProxy,代理是否可以通過ThreadLocal訪問,如果exposeProxy屬性為true,則可通過AopContext.currentProxy()方法獲得代理。

          ?? ● aopProxyFactory,所使用的AopProxyFactory具體實現。該參數用來指定使用動態代理、CGLIB或其他代理策略。默認選擇動態代理或CGLIB。一般不需要指定該屬性,除非需要使用新的代理類型,才指定該屬性。

          配置ProxyFactoryBean工廠bean時,還需要指定它的特定屬性,ProxyFactoryBean的特定屬性如下所示:

          ?? ● proxyInterfaces,接口名的字符串數組。如果沒有確定該參數,默認使用CGLIB代理。

          ?? ● interceptorNames,處理名的字符串數組。此處的次序很重要,排在前面的處理,優先被調用。此處的處理名,只能是當前工廠中處理的名稱,而不能使用bean引用。處理名字支持使用通配符(*)。

          ?? ● singleton,工廠是否返回單態代理。默認是true,無論 getObject()被調用多少次,將返回相同的代理實例。如果需要使用有狀態的處理——例如,有狀態的mixin,可改變默認設置,prototype處理。

          posted on 2009-07-19 10:15 jadmin 閱讀(71) 評論(0)  編輯  收藏

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


          網站導航:
           
          主站蜘蛛池模板: 石台县| 韶关市| 遂昌县| 从江县| 临夏市| 柳州市| 交城县| 陆良县| 永康市| 峨眉山市| 荆门市| 海原县| 巴林左旗| 遵义县| 淳化县| 华坪县| 舒兰市| 肥东县| 宜章县| 开远市| 噶尔县| 石屏县| 璧山县| 收藏| 买车| 元谋县| 沂水县| 峡江县| 额尔古纳市| 孝昌县| 石楼县| 精河县| 宝兴县| 辽中县| 定结县| 和林格尔县| 清水县| 伊金霍洛旗| 隆化县| 桂阳县| 名山县|